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

Updates #199

Merged
merged 16 commits into from
May 16, 2017
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
^.*\.Rproj$
^\.Rproj\.user$
^tools$
^srcjs$
^tests-manual$
^\.travis\.yml$
^appveyor\.yml$
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Imports:
shiny (>= 0.12.1),
htmltools (>= 0.2.6)
BugReports: https://github.com/rstudio/shinydashboard
RoxygenNote: 5.0.1
RoxygenNote: 6.0.1
35 changes: 32 additions & 3 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,33 @@
shinydashboard 0.5.3.9000
shinydashboard 0.6
=========================

This release of shinydashboard was aimed at both fixing bugs and also bringing the package up to speed with users' requests and Shiny itself (especially fully bringing [bookmarkable state](https://shiny.rstudio.com/articles/bookmarking-state.html) to shinydashboard's sidebar). In addition to the changes listed below, we also added a [new "Behavior" section to the shinydashboard website](https://rstudio.github.io/shinydashboard/behavior.html) to explain this release's two biggest new features.


## Full changelog

### New features

* Address [#179](https://github.com/rstudio/shinydashboard/issues/179) support for bookmarking the expanded/collapsed state of the whole sidebar. (commit [e71c93f](https://github.com/rstudio/shinydashboard/pull/199/commits/e71c93fa7a71f229e725efd4a7867e431cd57679))

* Added Shiny input to keep track of which sidebar `menuItem` is expanded (if any), which makes bookmarking the exact state of the sidebar trivial. (commit [6901b90](https://github.com/rstudio/shinydashboard/pull/199/commits/6901b90b8c866b89d02514cfc01fdfab88175602))

### Minor new features and improvements

* Addressed [#165](https://github.com/rstudio/shinydashboard/issues/165): added a new optional argument, called `headerText` to the `dropdownMenu()` function. If provided by the user, this text (instead of the default) will be shown on the header of the menu (only visible when the menu is expanded). See `?dropdownMenu` for more details. [#207](https://github.com/rstudio/shinydashboard/pull/207)

* Split JS files. (commit [ea91503](https://github.com/rstudio/shinydashboard/pull/199/commits/ea915038ae2126f48c15e3aac41782a22b16c506)). More updates to Gruntfile and structure. (commit [4e80616](https://github.com/rstudio/shinydashboard/pull/199/commits/4e80616c5b3aa0dc73022dc815288b5ba7c35be0))

* Better shown/hidden mechanic for Shiny inputs inside collapsible `menuItem`s. (commit [6901b90](https://github.com/rstudio/shinydashboard/pull/199/commits/6901b90b8c866b89d02514cfc01fdfab88175602))

* Added hack on adminLTE/app.js in order to make the `slideUp`/`slideDown` css transitions look reasonable when its content is initially empty (use case is for hidden Shiny outputs that are not rendered until the first time the `menuItem` is expanded and reveal them -- i.e. first time that `trigger("shown")` is called). (commit [25725a6](https://github.com/rstudio/shinydashboard/pull/199/commits/25725a67ce3dd841786dd82b0e46624c6a7d4722))

* Added manual tests for bookmarking and the shown/hidden events that happen on the sidebar. (commit [9e3e55d](https://github.com/rstudio/shinydashboard/pull/199/commits/9e3e55de8cc63d4bdd179251cd53eeb845441d3d))

### Bug fixes

* Fixed [#71](https://github.com/rstudio/shinydashboard/issues/71) and [#87](https://github.com/rstudio/shinydashboard/issues/87): detect and enforce selected tab for dynamic sidebar menus by calling `ensureActivatedTab()` for these. (commit [9b88a79](https://github.com/rstudio/shinydashboard/pull/199/commits/9b88a790df058432165ca3b483b5bbfe1d267326))

* Fixed [#127](https://github.com/rstudio/shinydashboard/issues/127) and [#177](https://github.com/rstudio/shinydashboard/issues/177): previously, if `dashboardSidebar()` was called with an explicit `width` parameter, mobile rendering would look weird (the sidebar wouldn't completely disappear when it was collapsed, and content in the dashboard body would be hidden under the still-visible sidebar). ([#204](https://github.com/rstudio/shinydashboard/pull/204))

* Fixed [#79](https://github.com/rstudio/shinydashboard/issues/79): Re-enable slight css transition when the sidebar is expanded/collapsed. ([#205](https://github.com/rstudio/shinydashboard/pull/205)).
Expand All @@ -15,10 +40,14 @@ shinydashboard 0.5.3.9000

* Fixed [#62](https://github.com/rstudio/shinydashboard/issues/62): make images resize when the sidebar collapses/expands. [#185](https://github.com/rstudio/shinydashboard/pull/185)

* Addressed [#178](https://github.com/rstudio/shinydashboard/issues/178): switch from `npm` to `yarn`. Also upgraded all yarn packages to the `latest` tag (all major changes). [#184](https://github.com/rstudio/shinydashboard/pull/184)

* Fixed [#176](https://github.com/rstudio/shinydashboard/issues/176) (making buttons look good on the sidebar) by giving Shiny action buttons and links some margin space. ([#182](https://github.com/rstudio/shinydashboard/pull/182))

### Library updates

* Update documentation to newest version of roxygen. (commit [#541d3c1](https://github.com/rstudio/shinydashboard/pull/199/commits/541d3c13467446c8e89b178d95b0984729559059))

* Addressed [#178](https://github.com/rstudio/shinydashboard/issues/178): switch from `npm` to `yarn`. Also upgraded all yarn packages to the `latest` tag (all major changes). [#184](https://github.com/rstudio/shinydashboard/pull/184)

* Updated to AdminLTE 2.3.11. ([#181](https://github.com/rstudio/shinydashboard/pull/181))

shinydashboard 0.5.3
Expand Down
6 changes: 3 additions & 3 deletions R/dashboardPage.R
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ dashboardPage <- function(header, sidebar, body, title = NULL,
body
)

# if the sidebar has the class "start-collapsed", it means that the user set
# the `collapsed` argument of `dashboardSidebar` to TRUE
collapsed <- "start-collapsed" %in% strsplit(sidebar$attribs$class, " ")[[1]]
# if the sidebar has the attribute `data-collapsed = "true"`, it means that
# the user set the `collapsed` argument of `dashboardSidebar` to TRUE
collapsed <- findAttribute(sidebar, "data-collapsed", "true")

addDeps(
tags$body(
Expand Down
55 changes: 48 additions & 7 deletions R/dashboardSidebar.R
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,20 @@ dashboardSidebar <- function(..., disable = FALSE, width = NULL, collapsed = FAL
'))))
}

# If we're restoring a bookmarked app, this holds the value of whether or not the
# sidebar was collapsed. If this is not the case, the default is whatever the user
# specified in the `collapsed` argument.
dataValue <- shiny::restoreInput(id = "sidebarCollapsed", default = collapsed)
if (disable) dataValue <- TRUE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a comment here to about the transition issue that this is a workaround for?

dataValueString <- if (dataValue) "true" else "false"

# The expanded/collapsed state of the sidebar is actually set by adding a
# class to the body (not to the sidebar). However, it makes sense for the
# `collapsed` argument to belong in this function. So this information is
# just passed through (also as a class) to the `dashboardPage()` function
tags$aside(class = paste("main-sidebar", if (collapsed) "start-collapsed"),
custom_css,
# just passed through (as the `data-collapsed` attribute) to the
# `dashboardPage()` function
tags$aside(
class = "main-sidebar", `data-collapsed` = dataValueString, custom_css,
tags$section(
class = "sidebar",
`data-disable` = if (disable) 1 else NULL,
Expand Down Expand Up @@ -236,6 +244,13 @@ sidebarSearchForm <- function(textId, buttonId, label = "Search...",
#' @param selected If \code{TRUE}, this \code{menuItem} or \code{menuSubItem}
#' will start selected. If no item have \code{selected=TRUE}, then the first
#' \code{menuItem} will start selected.
#' @param expandedName A unique name given to each \code{menuItem} that serves
#' to indicate which one (if any) is currently expanded. (This is only applicable
#' to \code{menuItem}s that have children and it is mostly only useful for
#' bookmarking state.)
#' @param startExpanded Should this \code{menuItem} be expanded on app startup?
#' (This is only applicable to \code{menuItem}s that have children, and only
#' one of these can be expanded at any given time).
#' @param ... For menu items, this may consist of \code{\link{menuSubItem}}s.
#' @param .list An optional list containing items to put in the menu Same as the
#' \code{...} arguments, but in list format. This can be useful when working
Expand Down Expand Up @@ -263,7 +278,9 @@ sidebarMenu <- function(..., id = NULL, .list = NULL) {
# Given a menuItem and a logical value for `selected`, set the
# data-start-selected attribute to the appropriate value (1 or 0).
selectItem <- function(item, selected) {
if (length(item$children) == 0) {

# in the cases that the children of menuItems are NOT menuSubItems
if (is.atomic(item) || length(item$children) == 0) {
return(item)
}

Expand All @@ -274,6 +291,7 @@ sidebarMenu <- function(..., id = NULL, .list = NULL) {
# data-start-selected="1". The []<- assignment is to preserve
# attributes.
item$children[] <- lapply(item$children, function(child) {

# Find the appropriate <a> child
if (tagMatches(child, name = "a", `data-toggle` = "tab")) {
child$attribs[["data-start-selected"]] <- value
Expand Down Expand Up @@ -328,18 +346,25 @@ sidebarMenu <- function(..., id = NULL, .list = NULL) {
item
})
}
# This is a 0 height div, whose only purpose is to hold the tabName of the currently
# selected menuItem in its `data-value` attribute. This is the DOM element that is
# bound to tabItemInputBinding in the JS side.
items[[length(items) + 1]] <- div(id = id,
class = "sidebarMenuSelectedTabItem", `data-value` = selectedTabName %OR% "null")
}

# Use do.call so that we don't add an extra list layer to the children of the
# ul tag. This makes it a little easier to traverse the tree to search for
# selected items to restore.
do.call(tags$ul, c(id = id, class = "sidebar-menu", items))
do.call(tags$ul, c(class = "sidebar-menu", items))
}

#' @rdname sidebarMenu
#' @export
menuItem <- function(text, ..., icon = NULL, badgeLabel = NULL, badgeColor = "green",
tabName = NULL, href = NULL, newtab = TRUE, selected = NULL) {
tabName = NULL, href = NULL, newtab = TRUE, selected = NULL,
expandedName = as.character(gsub("[[:space:]]", "", text)),
startExpanded = FALSE) {
subItems <- list(...)

if (!is.null(icon)) tagAssert(icon, type = "i")
Expand Down Expand Up @@ -394,6 +419,18 @@ menuItem <- function(text, ..., icon = NULL, badgeLabel = NULL, badgeColor = "gr
)
}

# If we're restoring a bookmarked app, this holds the value of what menuItem (if any)
# was expanded (this has be to stored separately from the selected menuItem, since
# these actually independent in AdminLTE). If no menuItem was expanded, `dataExpanded`
# is NULL. However, we want to this input to get passed on (and not dropped), so we
# do `%OR% ""` to assure this.
default <- if (startExpanded) expandedName else ""
dataExpanded <- shiny::restoreInput(id = "sidebarItemExpanded", default) %OR% ""

# If `dataExpanded` is not the empty string, we need to check that it is eqaul to the
# this menuItem's `expandedName``
isExpanded <- nzchar(dataExpanded) && (dataExpanded == expandedName)

tags$li(class = "treeview",
a(href = href,
icon,
Expand All @@ -403,7 +440,11 @@ menuItem <- function(text, ..., icon = NULL, badgeLabel = NULL, badgeColor = "gr
# Use do.call so that we don't add an extra list layer to the children of the
# ul tag. This makes it a little easier to traverse the tree to search for
# selected items to restore.
do.call(tags$ul, c(class = "treeview-menu", subItems))
do.call(tags$ul, c(
class = paste0("treeview-menu", if (isExpanded) " menu-open" else ""),
style = paste0("display: ", if (isExpanded) "block;" else "none;"),
`data-expanded` = expandedName,
subItems))
)
}

Expand Down
4 changes: 3 additions & 1 deletion R/deps.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ appendDependencies <- function(x, value) {
addDeps <- function(x) {
if (getOption("shiny.minified", TRUE)) {
adminLTE_js <- "app.min.js"
shinydashboard_js <- "shinydashboard.min.js"
adminLTE_css <- c("AdminLTE.min.css", "_all-skins.min.css")
} else {
adminLTE_js <- "app.js"
shinydashboard_js <- "shinydashboard.js"
adminLTE_css <- c("AdminLTE.css", "_all-skins.css")
}

Expand All @@ -28,7 +30,7 @@ addDeps <- function(x) {
htmlDependency("shinydashboard",
as.character(utils::packageVersion("shinydashboard")),
c(file = system.file(package = "shinydashboard")),
script = "shinydashboard.js",
script = shinydashboard_js,
stylesheet = "shinydashboard.css"
)
)
Expand Down
21 changes: 21 additions & 0 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,24 @@ tagMatches <- function(item, ..., id = NULL, name = NULL, class = NULL) {

TRUE
}

# This function takes a DOM element/tag object and reccurs within it until
# it finds a child which has an attribute called `attr` and with value `val`
# (and returns TRUE). If it finds an element with an attribute called `attr`
# whose value is NOT `val`, it returns FALSE. If it exhausts all children
# and it doesn't find an element with an attribute called `attr`, it also
# returns FALSE
findAttribute <- function(x, attr, val) {
if (is.atomic(x)) return(FALSE) # exhausted this branch of the tree

if (!is.null(x$attribs[[attr]])) { # found attribute called `attr`
if (identical(x$attribs[[attr]], val)) return(TRUE)
else return(FALSE)
}

if (length(x$children) > 0) { # recursion
return(any(unlist(lapply(x$children, findAttribute, attr, val))))
}

return(FALSE) # found no attribute called `attr`
}
2 changes: 1 addition & 1 deletion inst/AdminLTE/AdminLTE.min.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion inst/AdminLTE/_all-skins.min.css

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions inst/AdminLTE/app.js
100755 → 100644

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions inst/AdminLTE/app.js.map

Large diffs are not rendered by default.

Loading