---
title: "Manage tabs"
linkTitle: "Manage tabs"
type: docs
weight: 2
---
*****

## SPS tabs

In SPS, **tab** is the basic component of a functionality unit. For example, all the [Modules](/sps/modules/)
in SPS are complex tabs with many small sub-tabs, the [Canvas](/sps/canvas/) is another independent tab providing 
image editing features and an *user custom tab* is also a SPS tab.

From the developer's view, all SPS tabs are [Shiny Modules{blk}](https://shiny.rstudio.com/articles/modules.html). 

To understand how SPS tabs work, we will demonstrate with a SPS project. For demo purpose, we are using the 
`/tmp` folder but one should use a regular location instead of the temp in a  real 
case. 

```{r collapse=TRUE}
suppressPackageStartupMessages(library(systemPipeShiny))
spsInit(app_path = tempdir(), project_name = "tab_demo", overwrite = TRUE, change_wd = FALSE)
## save project path 
(sps_dir <- file.path(tempdir(), "tab_demo"))
```

To reproduce code locally, run the following chunk instead. 

```{r eval=FALSE}
library(systemPipeShiny)
spsInit()
sps_dir <- normalizePath(".")
```

## Tab registration
In SPS, all tabs are controlled by the *config/tabs.csv* file. To see what kind of 
tabs you have with current project. use the `spsTabInfo` function. It returns a tibble
of current tab information. 

```{r}
spsTabInfo(app_path = sps_dir)
```

- **tab_id**: A unique string ID
- **display_label**: for type is "core" or "module", this is only some description, but for you own custom tabs, 
this value will be used as a display tab name on left sidebar on SPS UI. 
- **type**: tab category, "core", "module" and "vs" (visualization).
- **type_sub**: more specific category, current only "plot" (plotting)
- **image**: If this is an user custom tab, providing an image path will display the image in 
visualization main tab gallery. If it not provided, a warning will be given on app 
starts and an "No image" image will be used like the following:

![vs_main](../vs_main.png)

- **displayed**: Internal use only
- **tab_file_name**: where the tab file is relative to the `R` folder. 
- **plugin**: Internal use only


##  Add a new custom tab 

SPS provides templates to help developers to create a small SPS tab  that 
1. The main purpose is to generate some plots
2. can be loaded into SPS framework easily
3. can interact with other SPS tabs (components), like the Canvas tab. 

When you initiate a SPS project, an example custom tab is created for you already.
You can find the tab file inside the `R` folder of the project root. When you start 
the app for the first time, you can easily find it from the left sidebar, or 
click on the "Custom tabs" to go to custom tab main tab. 

<center>

![](../new_tab_before.png)

<caption>Example custom tab</caption>

</center>

### Simple template
Under current SPS version, users are able to add custom tabs with the `spsNewTab` function. 
This function:
1. creates the tab file.
2. provides a nice template. 
3. Helps you to register tab information to `tabs.csv`

```{r collapse=TRUE}
spsNewTab(tab_id = "vs_new", tab_displayname = "New tab demo", app_path = sps_dir)
```

If you are running the code locally, and are using Rstudio, the tab file will be opened 
automatically for you. 

In your `global.R`, add the new tab to confirm you want to load it, and then restart 
you app. Scroll down to the bottom, you should see:

```{r, eval=FALSE}
# add "vs_new" in `tabs`
sps_app <- sps(
    tabs = c("vs_example", "vs_new"),
    server_expr = {
        msg("Custom expression runs -- Hello World", "GREETING", "green")
    }
)

```

<center>

![](../new_tab_after.png)

<caption>Load new custom tabs</caption>

</center>

By default, it uses the simple template, which contains the `spsEzUI` and  `spsEzServer` functions. 
We have provided commented instructions on how to fill each argument. 

#### UI
```{r eval=FALSE}
spsEzUI(
  desc = "xxx",
  tab_title = "xxx",
  plot_title = "xxx",
  plot_control =  shiny::tagList(
    xxx
  )
)
```


The only augment new users need to take some time to learn is adding `plot_control`.
Developers need to add some Shiny UI components to let users control how the plotting is 
done with interactive options. Basic use can be learned in 5 minutes on
[Shiny website{blk}](https://shiny.rstudio.com/articles/basics.html) and [Shiny book{blk}](https://mastering-shiny.org/basic-ui.html)

#### Server
```{r eval=FALSE}
spsEzServer(
    plot_code = {
        # data passed from data loading is a reactiveValues object, data stored in `mydata$data`
        plot_data <- mydata$data
        # some validations, make sure users give you the right data format
        spsValidate({
            stopifnot(inherits(plot_data, "data.frame"))                        # require a dataframe
            stopifnot(nrow(plot_data) > 1)                                      # has least one row
            if (!all(c("Sepal.Length", "Sepal.Width") %in% colnames(plot_data)))# has two required columns
                stop("Require column 'Sepal.Length' and 'Sepal.Width'")

            TRUE # give it a TRUE if all checks passed.
            },
            verbose = FALSE # only show messages when fail
        )
        # actual plot code
        ggplot2::ggplot(plot_data) +
            ggplot2::geom_point(ggplot2::aes(x = Sepal.Length, y = Sepal.Width)) +
            # grab user defined title from plot control by `input$+control_ID`,
            # no need to add `ns()` on server end.
            ggplot2::ggtitle(input$plot_title)
    },
    other_server_code = {}
)
```

For the server code, users only need to focus on the plotting code. The only very important thing 
developers need to remember is that the plotting data been passed to this function 
is stored in a [reactiveValues{blk}](https://shiny.rstudio.com/reference/shiny/0.11/reactiveValues.html)
object and it is called `mydata$data`. Usually we assign it to a new value so it can 
be used easily downstream, like `plot_data <- mydata$data`.

Some validation is recommended before reaching the plotting code. You would never know what 
kind of dataset users upload. It is always good to check if users' uploads meet the 
requirements. In SPS you can use the `spsValidate` function or use Shiny's default [validate{blk}](https://shiny.rstudio.com/reference/shiny/0.14/validate.html)
function (`spsValidate` is discussed in [developer tools](/sps/dev/spscomps/server/)). 

### Full template
For some developers who already has experience with Shiny, and would like to make more 
complex customization, using the full template enables you to change every detail on
the new tab. Simply add the `template = "full"` argument.

```{r collapse=TRUE}
spsNewTab(
  tab_id = "vs_new_full", 
  tab_displayname = "New tab demo",
  template = "full",
  app_path = sps_dir)
```
You can see the full template is a lot longer than the simple template:
```{r}
simple_len <- R.utils::countLines(file.path(sps_dir, "R", "tab_vs_new.R"))
full_len <- R.utils::countLines(file.path(sps_dir, "R", "tab_vs_new_full.R"))
spsinfo(glue::glue("Simple template has {simple_len} lines"), TRUE)
spsinfo(glue::glue("Full template has {full_len} lines"), TRUE)
```

## Tab registeration

In your *global.R*, scroll down to the bottom, you should see:

```{r, eval=FALSE}
sps_app <- sps(
    tabs = c("vs_example"),
    server_expr = {
        msg("Custom expression runs -- Hello World", "GREETING", "green")
    }
)

```

This is the SPS main function. You can load/unload custom tabs by providing tab IDs in `tabs` argument, like
`c("tab1", "tab2)`. Open `config/tabs.csv` or use `spsTabInfo()` to see what tabs IDs can be load and other
tab information. Essential framework tabs (*core*) and built-in modules (*modules*) are loaded automatically. However, you can choose to [unload core and modules tabs](../displaytabs), 
and [overwrite core tabs](../overwritetabs) by changing some [SPS options](../config). 

## Tab naming
Once a tab ID is provided in the `sps` function `tabs` argument, when the function
runs, it is looking for tab information inside this `tabs.csv`, like the display name 
and tab image. Then it will search for the UI and server function in the enviornment. 
It is expecting a UI function named `tab_id` + `UI` -> `tab_idUI` and the server
`tab_id` + `Server` -> `tab_idServer`. If you did not use the `spsNewTab` function 
to generate the new tab, make sure you name your tab UI and server in this pattern. 



