---
title: "Creating a new framework-based application"
author: "Dr. Connie Brett"
date: "`r Sys.Date()`"
output:
rmarkdown::html_vignette:
toc: TRUE
toc_depth: 3
css: "style.css"
vignette: >
%\VignetteIndexEntry{Creating a new framework-based application}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
# Overview
## Purpose
This framework was created for the express purpose of making it easy for shiny
developers to create consistent-looking and functioning applications. This is
important because it reduces the time a user must spend to learn the interface
for new applications (thus reducing application support time and increasing
the user's satisfaction across applications).
In addition to creating a consistent UI experience this framework reduces
development time for new applications by removing some of the boilerplate
aspects of new applications such as alerting, logging, etc.
## UI Sections
*1: Left Sidebar*
The left sidebar is reserved for configuration options, settings, and general user
application controls and functionality that will affect the body of the
application.
There are two tabs in the sidebar **Basic** and **Advanced**. The *Basic* tab
is intended to be used for commonly accessed options and settings whereas the
*Advanced* tab is reserved for less-common options and settings. The tab
names (or labels) can be changed. Both tabs are optional and if one of them or both
are missing there will be no tabs shown (the sidebar will not have tabbed areas).
The left sidebar can be collapsed in desktop mode to maximize the user's view of the
application body when they do not immediately need the configuration options and
settings. Also, it is possible to create an application without a left sidebar.
*2: Body*
This is the main body area for applications where charts and figures, tables,
etc. are placed by the application developer. If desired the developer can
add tabbed pages, etc.
*3: Right Sidebar*
The right sidebar is, like the left sidebar, reserved for configuration options, settings,
and general user application controls and functionality that will affect the body of the
application.
Users can create their own tabs in the right sidebar up to a maximum of 5.
The right sidebar is collapsed by default, it can be opened by clicking on the (customizable) icon.
**Note:** The blue area running across the top of the application is the header bar. This
area is reserved for framework use.
## Features
### New Application Template Generation
* **Empty application** - ready for the user to drop in their code!
* **Sample application** - demonstrates framework features in a working application
### Built-in Conveniences
#### Responsive UI
The application layout will adapt to user device size automatically
#### Application Busy Indicator
This indicator shows in the header bar automatically when the shiny application
is busy. There is no application developer code necessary to tie into this
functionality.
#### Application Reset
There is a button placed by the framework at the bottom of the Advanced sidebar
tab. This button is automatically wired to reset the application to the initial
session state. The user is given a warning (as an alert on the Advanced tab)
and the reset is delayed (default = 5s) to allow the user to cancel the reset.
Reset requests and cancellations are logged automatically.
#### Logging & Log Display
The framework makes it easy to log user actions. Framework user interactions
are automatically logged. The user interaction log is automatically shown in
a (collapsed) box at the bottom of the application body but can be turned off
by setting userlog = FALSE in *set_app_parameters* in *program/global.R*.
```{r, eval=F}
# Add a user action to the log
loginfo("Your Information Message with %s, %s parameters", parm1, parm2, logger = ss_userAction.Log)
logwarn("Your Warning Message!", logger = ss_userAction.Log)
```
#### Alerting
There are four standardized locations for user-alerts: the top of the basic
sidebar tab, the top of the advanced sidebar tab, the top of the body and at
the top of the right sidebar.
All the alerts are dismissible by the user and can be colored by setting the
status.
Alerts can accumulate (i.e. append to each other) or replace previous alerts.
```{r, eval=F}
# Alert on Sidebar>Basic tab
createAlert(session, "sidebarBasicAlert",
style = "info",
content = "Basic Alert Text")
# Alert on Sidebar>Advanced tab
createAlert(session, "sidebarAdvancedAlert",
style = "danger",
content = "Advanced Alert Text")
# Alert in the Body Area
createAlert(session, "bodyAlert", style = "success",
content = "Body Alert Text", append = FALSE)
# Alert on Right Sidebar
createAlert(session, "sidebarRightAlert",
style = "error",
content = "Error Alert Text")
```
#### Styling
##### Overview
Different parts of the generated application can be customized with a custom yaml file called *periscope_style.yaml* located under *www* folder as follow:
##### Usage
* User can update the values for **periscope_style.yaml** then restart the application so new changes can take affect.
* User can pass an existing **periscope_style.yaml** from an existing app to new one through passing its location to `custom_theme_file` parameter in `create_new_application` method.
* The sample applications contain a section to explore updating some styles interactively:
* The generated yaml file for blank applications will contain no values for the properties -- blank application will use default style options unless they are customized.
*See the Creating a Sample Application and Creating your Application sections for an example*
### Shiny Modules
#### downloadFile
This is a high-functionality set of custom-styled buttons with the built-in
ability to download data in different file formats.
*See the downloadFile Vignette for more detailed information*
#### downloadableTable
This is a high-functionality custom styled table that includes a
downloadFileButton linked to the data.
*See the downloadableTable Vignette for more detailed information*
#### downloadablePlot
This is a custom plot output that is paired with a linked downloadFile button
which allows the user to download the plot in different file formats.
*See the downloadablePlot Vignette for more detailed information*
# Creating a Sample Application
## Step 1: Generate
```{r, eval=F}
library(periscope)
app_dir = tempdir()
create_new_application(name = 'mytestapp', location = app_dir, sampleapp = TRUE)
# application without a left sidebar
create_new_application(name = 'mytestapp', location = app_dir, sampleapp = TRUE, leftsidebar = FALSE)
# application without a reset button
create_new_application(name = 'mytestapp', location = app_dir, sampleapp = TRUE, resetbutton = FALSE)
# application with a right sidebar using the default icon
create_new_application(name = 'mytestapp', location = app_dir, sampleapp = TRUE, rightsidebar = TRUE)
# application with a right sidebar using a custom icon
create_new_application(name = 'mytestapp', location = app_dir, sampleapp = TRUE, rightsidebar = "table")
# application with a custom style file
create_new_application(name = 'mytestapp', location = app_dir, sampleapp = TRUE, custom_theme_file = "periscope_style.yaml")
```
This generates a default sample application optionally with a left/right sidebar in a subdirectory named *mytestapp*
at the specified location. The location must exist when calling this function.
*Note*: If the *mytestapp* directory in this location already exists it will not
be overwritten - the function will give a warning and exit without modifying the
user's system.
## Step 2: Run
```{r, eval=F}
runApp(paste(app_dir, 'mytestapp', sep = .Platform$file.sep))
```
The application should run in either the viewer or browser (depending on system
preferences/settings). It will contain help text, test buttons, a sample log,
etc. You can use this sample application to explore the functionality and code!
# Creating your Application
## Step 1: Generate a Blank Application
```{r, eval=F}
library(periscope)
app_dir = tempdir()
create_new_application(name = 'mytestapp', location = app_dir)
# application without a left sidebar
create_new_application(name = 'mytestapp', location = app_dir, leftsidebar = FALSE)
# application without a reset button
create_new_application(name = 'mytestapp', location = app_dir, resetbutton = FALSE)
# application with a right sidebar using the default icon
create_new_application(name = 'mytestapp', location = app_dir, rightsidebar = TRUE)
# application with a right sidebar using a custom icon
create_new_application(name = 'mytestapp', location = app_dir, rightsidebar = "table")
# application with a custom style file
create_new_application(name = 'mytestapp', location = app_dir, custom_theme_file = "periscope_style.yaml")
```
This generates a default blank application optionally with a left/right sidebar in a subdirectory named *mytestapp*
at the specified location. The location must exist when calling this function.
*Note*: If the *mytestapp* directory in this location already exists it will not
be overwritten - the function will give a warning and exit without modifying the
user's system.
## Step 2: Run (optional)
```{r, eval=F}
runApp('mytestapp', appDir = app_dir)
```
It is recommended to run the empty application after creation to ensure a
proper setup before beginning to customize the application.
## Step 3: Customize
All user customization is done in the files in the **program** subdirectory.
We'll walk through a few examples of how to add components and functionality
as in a typical shiny application but using this framework.
### program/global.R
Set your application title, application version, titleinfo, change the loglevel and optionally turn
off the userlog using *set_app_parameters()*.
The *app_version* parameter in this function has a default value of "1.0.0". It is recommended to follow the best practice in R's packaging versioning. This means that a version consists of 3 numbers, *<major>.<minor>.<patch>*.
The *titleinfo* parameter in this function can be:
* NULL - The application title will be plain text with no extra functionality
* URL (character string) - The application title will be a clickable link that
will send the user to an external URL location. Any html-valid url can be used
to link to an accessible file location, http location, mailto: functionality or
javascript.
* HTML value - The html will be placed into a pop-up modal window that will be
shown when the user clicks the title link. Create the value using the
shiny::HTML() function.
***Important:*** Any variables or functions placed into this file are globally
scoped and will be available to ui and server functions across all user
sessions.
```{r, eval=F}
# Plain text title
set_app_parameters(title = "My Application Title")
# Application Title links to an external url
set_app_parameters(title = "My Application Title",
titleinfo = "http://www.somelocation.com")
# Application Title links to a modal window with HTML content
set_app_parameters(title = "My Application Title",
titleinfo = HTML("This is information about this application
",
"Author: Me
",
"Date: ", Sys.Date(), "
"))
```
### program/ui_sidebar.R
Create UI components for the left sidebar and register them with the framework using
a call to *add_ui_sidebar_basic()* or *add_ui_sidebar_advanced()* according to
which tab they should appear on in the sidebar.
If *add_ui_sidebar_advanced()* is not called there will be no second tab in the
sidebar and the tabs will not be shown.
```{r, eval=F}
# -- Create UI sidebar Elements
s1 <- div( helpText(align = "center", "Sample UI Text"),
selectInput("sample1", "A Select", c("A", "B", "C")) )
s2 <- div( ui_tooltip("sample2_tt", "Pick An Option", "Popup Text"),
radioButtons("sample2", NULL, c("A", "B", "C")) )
# -- Register Basic Elements in the ORDER SHOWN in the UI
add_ui_sidebar_basic(list(s1, s2), append = FALSE)
# -- Change the Label of the Basic Tab
add_ui_sidebar_basic(list(s1, s2), append = FALSE, tabname = "Options")
```
### program/ui_sidebar_right.R
Create UI components for the right sidebar and register them with the framework using
a call to *add_ui_sidebar_right()*.
```{r, eval=F}
# -- Create UI sidebar Elements
s1 <- rightSidebarTabContent(id = 1,
icon = "desktop",
title = "Tab 1",
active = TRUE,
div(helpText(align = "center",
"Sample UI Text"),
selectInput("sample1",
"A Select",
c("A", "B", "C"))))
# -- Register Basic Elements in the ORDER SHOWN in the UI
add_ui_sidebar_right(list(s1), append = FALSE)
```
### program/ui_body.R
Create UI components for the body and register them with the framework using a
call to *add_ui_body()*. Use box elements for consistent presentation of your
UI sections. Remember that there are 12 "blocks" across in the body.
```{r, eval=F}
body1 <- box( id = "bodyElement1",
title = "Box 1",
width = 8, #2/3 of the width
status = "primary", #colored bar at the top
collapsible = TRUE,
collapsed = FALSE,
htmlOutput("example1") )
body2 <- box( id = "bodyElement2",
title = "Box 2",
width = 4, #1/3 of the width
status = "danger", #colored bar at the top
collapsible = FALSE,
p("Some great text in paragraph format"),
pre("A pre-formatted (e.g. code) block"),
actionButton("exButton", label = "Example") )
add_ui_body(list(body1, body2))
```
In the above example UI elements are being added in 2 different ways. *body1*
uses an htmlOutput element to set a placeholder for the id "example1" which will
be setup in the server file. This is commonly used when there is need for a
dynamic UI element that cannot be setup ahead of the session (for example it is
dependent on data at runtime). *body2* defines the UI elements in place here.
This illustrates the two most common paradigms for creating shiny UI elements.
For more information see the Shiny documentation.
### program/server_local.R
Create your application functionality in this file. This corresponds to the
inside of the *shinyServer(...)* call in a traditional Shiny application and is
where the majority of your application code will reside. The variables and
functions in this file are isolated to a single application session.
**Variables Available**
* *input, output, session* - standard Shiny variables
* *ss_userAction.Log* - Reactive Logger object used to add items to the log
You can also call the *get_url_parameters(session)* function to retrieve anything passed
at the end of the URL.
```{r, eval=F}
source("program/fxn/makeplot.R")
#build the deferred UI from ui_body.R
output$example1 <- renderUI({
list(downloadFileButton("ex_d1", c("csv"), "Download CSV"),
hr(),
p("Some great explanatory text in my application"))
})
downloadFile("ex_d1", ss_userAction.Log, "mydownload", list(csv=get_ref_data))
observeEvent(input$exButton, {
loginfo("exButton Pressed!", logger = ss_userAction.Log)
createAlert(session, "bodyAlert",
style = "success",
content = "Example Button Pressed!")
})
```
### program/server_global.R
Create variables and functions in this file that are the same for **all**
sessions and users of your application. It corresponds to the area above the
*shinyServer(...)* call in a traditional Shiny application and generally there
will not be a lot of usage of this file because of its global scoping. However,
one of the common uses is to load reference data sets that are the same for all
users of the application.
```{r, eval=F}
ref_data <- read.csv("program/data/mydata.csv")
get_ref_data <- function() {
return(ref_data)
}
```
### Other
#### program/data (directory)
Use this directory to store data for your application. Note that a **.gitignore**
file has been added so that data is not accidentally versioned. If you want
to version files in this directory you will need to modify this file
accordingly.
#### program/fxn (directory)
Use this directory to store your .R files containing helper functions. Don't
forget to source these files in the appropriate place according to your scoping
needs. *(i.e. you would source a file in server_local.R to scope by user
session, server_global.R to scope across all sessions, and global.R to scope
across all sessions and UI)*
#### www/periscope_style.yaml
Updated this file values and restart app to customize application different parts styles.
# Additional Resources
**Application Gallery**
There is a gallery of example applications using the periscope framework at [http://periscopeapps.org](http://periscopeapps.org:3838)
**Vignettes**
* [downloadFile Module](downloadFile-module.html)
* [downloadableTable Module](downloadableTable-module.html)
* [downloadablePlot Module](downloadablePlot-module.html)