-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdocument.qmd
1213 lines (993 loc) · 36.7 KB
/
document.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Documentation {#sec-doc}
```{r}
#| eval: true
#| echo: false
#| include: false
source("_common.R")
```
```{r}
#| label: co_box_tldr
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "b",
look = "default", hsize = "1.10", size = "1.05",
header = " ![](images/roxygen2.png){height=40} TLDR",
fold = TRUE,
contents = "
Documenting app-package functions:
1. Make sure each function has documentation for the `@title`, `@description`, `@param`(s), `@return`, and `@examples` (if applicable)\n
2. Use Markdown for code, emphasis, hyperlinks, etc.\n
3. Regularly load and document to preview the help files:\n
    [<kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>L</kbd> / <kbd>D</kbd>]{style='font-weight: bold; font-size: 0.75em;'}
4. Link modules with `@seealso` and group functions with `@family`\n
5. Include any additional information in a new `@section`\n
"
)
```
---
This chapter covers documenting Shiny app functions with `roxygen2`. I'll start by introducing the fundamentals of the `roxygen2` syntax, but also touch on a few helpful tips specific to Shiny modules and standalone app functions.
```{r}
#| label: shinypak_apps
#| echo: false
#| results: asis
#| eval: true
shinypak_apps(regex = "^05", branch = "05_roxygen2")
```
The current structure of our `sap` package is displayed in the folder tree below:[^devtools-branch]
[^devtools-branch]: View the [`04_devtools` branch](https://github.com/mjfrigaard/sap/tree/04_devtools) and review the [previous chapter](development.qmd) for more information.
```{bash}
#| eval: false
#| code-fold: false
sap/
├── DESCRIPTION #<1>
├── NAMESPACE #<2>
├── R #<3>
│ ├── mod_scatter_display.R
│ ├── mod_var_input.R
│ └── utils.R #<3>
├── README.md #<4>
├── app.R #<5>
├── man #<6>
├── movies.RData #<7>
├── sap.Rproj #<8>
└── www #<9>
└── shiny.png #<10>
4 directories, 10 files
```
1. Package metadata (output from `devtools::document()`)
2. Package dependencies (output from `devtools::document()`)
3. Package code
4. Package overview (instructions for installation and use)
5. Script for launching application
6. Empty folder for help files
7. App data
8. Project file (for RStudio)
9. App resources
10. Image file
(*the `rsconnect/` folder from deploying `sap` has been removed*)
When we use `devtools::install()`, the following message regarding documentation is returned:
::: {style='font-weight: bold; font-size: 1.15em;'}
> *`No man pages found in package ‘sap’`*
:::
Documenting the functions in the `R/` folder will address this message, and I strongly encourage checking out the [`roxygen2` vignettes](https://roxygen2.r-lib.org/articles/rd.html) and the [chapter in R Packages, 2ed](https://r-pkgs.org/man.html#roxygen2-basics) for more information on documenting your app-package.
## roxygen2 basics {#sec-intro-roxygen2}
```{r}
#| label: git_box_04_devtools
#| echo: false
#| results: asis
#| eval: true
git_margin_box(
contents = "standard",
fig_pw = '60%',
branch = "04_devtools",
repo = 'sap')
```
[`roxygen2`](https://roxygen2.r-lib.org/) connects the package code (i.e., the `.R` files in the `R/` folder) to its documentation files (i.e., the `.Rd` files in the `man/` folder). Two pieces of `roxygen2` syntax to know are **comment blocks** and **tags**:
- Comment blocks are any lines beginning with `#' `
```{r}
#| eval: false
#| code-fold: false
#'
#'
#'
```
- Tags begin with `@`
```{r}
#| eval: false
#| code-fold: false
#'
#' @tag
#'
```
The `roxygen2` tags and comment blocks are placed above functions and used with `devtools::document()` to create help documentation:
```{r}
#| eval: false
#| code-fold: false
#'
#' @tag
#'
my_fun <- function() {
}
```
In the following sections, we'll cover some `roxygen2` basics using the `scatter_plot()` function in the `R/utils.R` file.
### File name alignment
Shiny apps (and most R projects) often contain utility functions in `helper.R` or `utils.R` files. Placing non-Shiny code in these files isn't a bad practice--it's is even encouraged in [Mastering Shiny](https://mastering-shiny.org/scaling-functions.html#file-organisation):
> "*You might want to collect smaller, simpler, functions into one place. I often use `R/utils.R` for this...*"
However, because we'll want to develop tests for `scatter_plot()`, we should follow the advice in [R Packages](https://r-pkgs.org/code.html#sec-code-style-organising),[^document-utils] and rename `R/utils.R` to `R/scatter_plot.R` as we transition to an app-package.[^document-test-files]
[^document-utils]: *'More often, a single `.R` file will contain multiple function definitions: such as a main function and its supporting helpers, a family of related functions, or some combination of the two.'* - [Organise functions into files, R Packages, 2ed](https://r-pkgs.org/code.html#sec-code-style-organising)
[^document-test-files]: Making `.R` file/function names brief but descriptive will also make writing and running tests easier.
```{r}
#| label: co_box_utils_files
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "b",
header = "**R/utils.R**",
look = "default", hsize = "1.10", size = "1.05",
contents = "
From now on, I've renamed `R/utils.R` to `R/scatter_plot.R`, so it aligns with the naming conventions in other R packages.
For a deep dive on `R/utils.R` files in R packages, I recommend reading [Dissecting R Package “Utility Belts”](https://rud.is/b/2018/04/08/dissecting-r-package-utility-belts/)
",
fold = TRUE
)
```
### Markdown support {#sec-roxygen2-md-support}
When we created our app-package with `create_package()`, support for markdown formatting in our package help files was automatically included by adding `Roxygen: list(markdown = TRUE)` to the `DESCRIPTION` file:
```{bash}
#| eval: false
#| code-fold: false
Package: sap
Version: 0.0.0.9500 #<1>
Type: Package
Title: Shiny App-Packages
Description: An R package with a collection of Shiny applications.
Author: John Smith [aut, cre]
Maintainer: John Smith <[email protected]>
License: GPL-3
RoxygenNote: 7.3.2
Encoding: UTF-8
Roxygen: list(markdown = TRUE) #<2>
```
1. Updated package version
2. Markdown support
If this field is not in your `DESCRIPTION` file, follow the instructions in the [`roxygen2` documentation](https://roxygen2.r-lib.org/articles/markdown.html#turning-on-markdown-support) for enabling markdown.
### Title & description {#sec-roxygen2-title}
```{r}
#| label: co_box_skeletons
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "g",
look = "default", hsize = "1.10", size = "1.05",
header = "Inserting roxygen2 skeletons",
contents = "
In RStudio ![](images/rstudio-icon.png){height=20}, you can insert an `roxygen2` skeleton into your function using **Code** > **Insert Roxygen Skeleton**
![Standard `roxygen2` skeleton](images/roxygen2_skeleton.png){width='100%' fig-align='center'}
",
fold = TRUE
)
```
The first two sections of `roxygen2` documentation are the title and description. These sections don't require tags--`roxygen2` will detect each section as long as there is at least one `#'` line separating them (and their contents don't extend past the length indicated in parentheses below):
```{r}
#| eval: false
#| code-fold: false
#' function title (one line)
#'
#' A short description... (one paragraph)
#'
```
The `@title` and `@description` for the `scatter_plot()` function stored in `R/utils.R` might look like this:
```{r}
#| eval: false
#| code-fold: false
#' Create scatter plot
#'
#' Custom [`ggplot2`](https://ggplot2.tidyverse.org/) function for building scatter plots in `sap`.
#'
```
```{r}
#| label: co_box_desc_details
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "g",
fold = TRUE,
look = "default", hsize = "1.10", size = "1.05",
header = "Function **@description** and **@details**",
contents = "
If the contents in the `@description` for a function requires more complicated formatting than a single paragraph (i.e., multiple paragraphs, lists, etc.), you can use the `@details` tag:
\`\`\`r
#' Create scatter plot
#'
#' @description
#' Custom [`ggplot2`](https://ggplot2.tidyverse.org/) function for building scatter plots in `sap`.
#'
#' @details
#' `scatter_plot()` is a custom `ggplot2` function written to handle string arguments from reactive inputs (i.e., `input$x` or `input$y`)
\`\`\`
")
```
### Parameters & return objects {#sec-roxygen2-param-return}
Document function arguments and outputs with `@param` and `@return`:
- **`@param`** should include the name and description of each function input (i.e., their type and what they do)[^param-docs]
```{r}
#| eval: false
#| code-fold: false
#' @param name description of its action
```
[^param-docs]: Read more about `@param` in the [arguments chapter](https://r-pkgs.org/man.html#arguments) of R Packages, 2ed.
- **`@return`** describes the type (or class) and structure of the function output[^return-docs]
```{r}
#| eval: false
#| code-fold: false
#' @return type/structure of the output
```
[^return-docs]: Read more about `@return` in the [return value chapter](https://r-pkgs.org/man.html#sec-man-returns) of R Packages, 2ed.
Below are examples for `@param` and `@return` in the `scatter_plot()` function stored in `R/utils.R`:
```{r}
#| eval: false
#| code-fold: false
#'
#' @param df `data.frame` or `tibble`
#' @param x_var string variable mapped to `x` axis
#' @param y_var string variable mapped to `y` axis
#' @param col_var string variable mapped to `color`
#' @param alpha_var number for point `alpha`
#' @param size_var number for point `size`
#'
#' @return A `ggplot2` plot object
```
To view how the `roxygen2` syntax will appear in the `.Rd` file, we'll need to document `sap`:
:::{.column-margin}
[To reduce the amount of repetitive code, I'll just be showing the keyboard shortcuts for each `devtools` function from this point forward]{style="font-style: italic; font-size: 1.05em"}
:::
```{r}
#| label: dev_key_document_basics
#| echo: false
#| results: asis
#| eval: true
hot_key("D")
```
I can see the `scatter_plot.Rd` file is written to the `man/` folder:
``` sh
ℹ Updating sap documentation
ℹ Loading sap
Writing scatter_plot.Rd
Documentation completed
```
An overview of what `devtools::document()` does is illustrated below:
```{=html}
<style>
.codeStyle span:not(.nodeLabel) {
font-family: monospace;
font-size: 1.5em;
font-weight: bold;
color: #9753b8 !important;
background-color: #f6f6f6;
padding: 0.2em;
}
</style>
```
```{mermaid}
%%| fig-cap: '`roxygen2` creates the `NAMESPACE` and help files in the `man/` folder'
%%| fig-align: center
%%{init: {'theme': 'neutral', 'themeVariables': { 'fontFamily': 'monospace', "fontSize":"13px"}}}%%
flowchart TB
subgraph Code["R/ Folder"]
Roxy2("<code>roxygen2</code> comments (<code>#'</code>)<br> & tags (<code>@</code>)")
end
Roxy2 --> Doc[["<strong>Run <code>devtools::document()</code></strong>"]]
Doc --> Parse("Parses <code>roxygen2</code> comments and tags")
subgraph Man["man/ Folder"]
Rd(".Rd Files")
end
subgraph Root["Package Root Folder"]
NS("NAMESPACE")
end
Parse --> Man
Parse --> Root
%% style Code fill:#8dd38d,stroke:none,rx:10,ry:10,font-size:13px
%% style Roxy2 fill:#8dd38d,stroke:none,rx:10,ry:10,font-size:13px
%% style Doc fill:#89D6FB,stroke:none,rx:10,ry:10,font-size:13px
%% style Parse fill:#89D6FB,stroke:none,rx:10,ry:10,font-size:13px
%% style Man fill:#8dd38d,stroke:none,rx:10,ry:10,font-size:13px
%% style NS fill:#8dd38d,stroke:none,rx:10,ry:10,font-size:13px
```
### .Rd files {#sec-rd-files}
When we open `man/scatter_plot.Rd`, we see it contains the following:
![The `scatter_plot.Rd` documentation file](images/doc_scatter_plot_man_rd.png){width='100%' fig-align='center'}
R documentation (`.Rd`) files have a formatting style similar to [(La)TeX](https://www.latex-project.org/), but `roxygen2` saves us from having to learn this syntax by automatically generating the `.Rd` files.
To view the help file, I can enter `?scatter_plot` in the console:
```{r}
#| eval: false
#| code-fold: false
?scatter_plot
```
![Help file](images/doc_scatter_plot_rd.png){width='100%' fig-align='center'}
Previewing the development versions of our documentation is a great way to verify the content in each `.Rd` file meets our expectations.
### Examples {#sec-roxygen2-examples}
`@examples` are unique because they include executable code that demonstrates how a particular function works. In the Posit Workbench IDE, `@examples` are especially helpful because they come with a clickable hyperlink (the `@examples` from `ggplot2::aes()` are below):
:::{#fig-doc_aes_run_examples layout-ncol=2 layout-valign="top"}
![**Run examples** hyperlink](images/doc_aes_examples.png){#fig-doc_aes_run_examples width='100%' fig-align='center'}
![Executed `@examples` code](images/doc_aes_run_examples.png){#fig-doc_aes_run_examples width='100%' fig-align='center'}
**Run examples** in help files
:::
Below is an example demonstrating how the `scatter_plot()` utility function works:
```{r}
#| eval: false
#| code-fold: false
#' @examples
#' scatter_plot(mtcars,
#' x_var = "mpg",
#' y_var = "disp",
#' col_var = "cyl",
#' alpha_var = 0.5,
#' size_var = 3)
```
```{r}
#| label: co_box_tags_tab_completion
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "g",
look = "default", hsize = "1.10", size = "1.05",
header = "Tab-completion for @tags",
contents = "
If you're documenting your package functions in Positron, tab-completion is your friend! The help text displayed with each tag ensures you're including the correct version (see `@example` vs. `@examples` below).
![Tab completion for `@example`](images/roxygen2_example.png){width=90%}
Read more [here](https://r-pkgs.org/man.html#sec-man-examples).
",
fold = TRUE
)
```
To preview the `@examples` in the help file, I'll `document()` and open the development `.Rd` file:
```{r}
#| label: dev_key_document_examples
#| echo: false
#| results: asis
#| eval: true
hot_key("D")
```
```{r}
#| eval: false
#| code-fold: false
?scatter_plot
```
![Preview of examples](images/doc_scatter_plot_examples.png){width='100%' fig-align='center'}
The **Run examples** hyperlink won't be active in the `.Rd` file preview, but reviewing the code in `@examples` allows me to correct any errors or typos early.
The `scatter_plot()` function now has a **Title**, **Description**, **Usage**, **Arguments**, **Value**, and **Examples** documented. Consider these tags as the minimum level of documentation for the functions in your app-package.
## Documenting app functions {#sec-document-app-functions}
Using Markdown in `roxygen2` can improve the clarity of our documentation, especially in Shiny app-packages, where modules and functions often need detailed explanations.
### See also {#sec-roxygen2-seealso}
When documenting Shiny modules, I tend to think of the audience as anyone looking to understand the *execution path* through an application. For example, in our `sap` application, the inputs are collected in the UI and returned from the `mod_var_input_server()` function:
Example for `mod_var_input_ui`:
```{r}
#| eval: false
#| code-fold: false
#' @seealso
#' - [`mod_var_input_server()`] for server-side logic.
#' - [`shiny::tagList()`](https://shiny.rstudio.com/reference/shiny/latest/tagList.html)
#' for containing UI elements.
#' - [`shiny::NS()`](https://shiny.rstudio.com/reference/shiny/latest/NS.html)
#' for namespacing.
```
```{=html}
<style>
.codeStyle span:not(.nodeLabel) {
font-family: monospace;
font-size: 1.5em;
font-weight: bold;
color: #9753b8 !important;
background-color: #f6f6f6;
padding: 0.2em;
}
</style>
```
```{mermaid}
%%| fig-cap: 'The `@seealso` tag'
%%| fig-align: center
%%{init: {'theme': 'neutral', 'themeVariables': { 'fontFamily': 'monospace', "fontSize":"13px"}}}%%
flowchart LR
mod_var_input_ui("UI Module:<br><code>mod_var_input_ui()</code>")
mod_var_input_server("Server Module:<br><code>mod_var_input_server()</code>")
mod_scatter_display_server("Server Module:<br><code>mod_scatter_display_server()</code>")
tag("Documentation:<br><code>shiny::tagList()</code>")
ns("Documentation:<br><code>shiny::NS()</code>")
mod_var_input_ui <-->|"@seealso"|mod_var_input_server
mod_var_input_ui <-->|"@seealso"|tag
mod_var_input_ui <-->|"@seealso"|ns
mod_var_input_server <-->|"@seealso"|mod_scatter_display_server
style mod_scatter_display_server font-size:13px
```
Example for `mod_var_input_server`:
```{r}
#| eval: false
#| code-fold: false
#' @seealso
#' - [`mod_var_input_ui()`] for the UI counterpart of this module.
#' - [`mod_scatter_display_server()`] for displaying the scatter plot.
```
```{=html}
<style>
.codeStyle span:not(.nodeLabel) {
font-family: monospace;
font-size: 1.5em;
font-weight: bold;
color: #9753b8 !important;
background-color: #f6f6f6;
padding: 0.2em;
}
</style>
```
```{mermaid}
%%| fig-cap: 'The `@seealso` tag'
%%| fig-align: center
%%{init: {'theme': 'neutral', 'themeVariables': { 'fontFamily': 'monospace', "fontSize":"13px"}}}%%
flowchart LR
mod_var_input_ui("UI Module:<br><code>mod_var_input_ui()</code>")
mod_var_input_server("Server Module:<br><code>mod_var_input_server()</code>")
mod_scatter_display_server("Server Module:<br><code>mod_scatter_display_server()</code>")
mod_var_input_server <-->|"@seealso"|mod_var_input_ui
mod_var_input_server <-->|"@seealso"|mod_scatter_display_server
style mod_scatter_display_server font-size:13px
```
With Markdown, you can use backticks for inline code, bulleted lists, and links for external references. To make references more readable and user-friendly, we can use `@seealso` to connect modules, utility functions, and links to external documentation.
### Family {#sec-roxygen2-family}
The `@family` tag works the same way, but can be used to group functions by type. Markdown gives us the ability to use bold or italic text for emphasis.
For the variable input module:
```{r}
#| eval: false
#| code-fold: false
#' @family **Variable Input Module**
```
For utility functions:
```{r}
#| eval: false
#| code-fold: false
#' @family *Utility Functions*
```
```{=html}
<style>
.codeStyle span:not(.nodeLabel) {
font-family: monospace;
font-size: 1.5em;
font-weight: bold;
color: #9753b8 !important;
background-color: #f6f6f6;
padding: 0.2em;
}
</style>
```
```{mermaid}
%%| fig-cap: 'The `@family` tag'
%%| fig-align: center
%%{init: {'theme': 'neutral', 'themeVariables': { 'fontFamily': 'monospace', "fontSize":"13px"}}}%%
flowchart TD
subgraph AppCom["<strong>Application Components</strong>"]
launch_app("Standalone Function:<br>launch_app()")
end
subgraph Vars["<strong>Variable Input Module</strong>"]
mod_var_input_ui("UI Module:<br>mod_var_input_ui()")
mod_var_input_server("Server Module:<br>mod_var_input_server()")
end
subgraph Graph["<strong>Plot Display Module</strong>"]
mod_scatter_display_ui("UI Module:<br>mod_scatter_display_ui()")
mod_scatter_display_server("Server Module:<br>mod_scatter_display_server()")
end
subgraph Utils["<strong>Utility Functions</strong>"]
scatter_plot("Utility Function:<br>scatter_plot()")
end
launch_app --> mod_var_input_ui --> mod_var_input_server
launch_app --> mod_scatter_display_ui --> mod_scatter_display_server --> scatter_plot
```
Markdown doesn’t drastically change `@family`, but it improves the consistency of formatting across related functions.
### Section {#sec-roxygen2-section}
Markdown shines in sections, where you can use headings (`###`), bold text, or lists to structure detailed explanations.
Example for `mod_var_input_server`:
```{r}
#| eval: false
#| code-fold: false
#' @section Inputs:
#' This function expects inputs provided via the `shiny` session namespace:
#'
#' - `y`: Variable for the y-axis.
#' - `x`: Variable for the x-axis.
#' - `z`: Variable for the color aesthetic.
#' - `alpha`: Transparency level of points (0-1).
#' - `size`: Size of the points.
#' - `plot_title`: Title for the plot.
#'
#' @section Outputs:
#' - A **reactive expression** that returns a list of selected inputs.
```
For `scatter_plot`:
```{r}
#| eval: false
#| code-fold: false
#' @section Usage:
#' Use this function to create a scatter plot as part of a Shiny app or in a
#' standalone script. This is particularly useful when integrated with reactive
#' inputs from Shiny modules.
```
#### Variable input
Below are full example of variable input module documented with Markdown. Note the use of `@family` and `interactive()` in the `@examples`:
**`mod_var_input_ui()`**:
```{r}
#| eval: false
#| code-fold: show
#| code-summary: 'show/hide mod_var_input_ui documentation'
#' Variable Input Module - UI
#'
#' Creates a UI for selecting variables and attributes for a scatter plot.
#'
#' @param id *(character)* Namespace ID for the module.
#'
#' @return A `shiny::tagList()` containing UI elements for variable
#' selection.
#'
#' @section Details:
#' `mod_var_input_ui()` provides UI controls for customizing scatter plots.
#' It includes:
#' - **Dropdowns**:
#' - X-axis variable
#' - Y-axis variable
#' - Color aesthetic variable
#' - **Sliders**:
#' - Transparency (`alpha`) ranging from 0 to 1.
#' - Point size ranging from 0 to 5.
#' - **Text Input**:
#' - Plot title.
#'
#' The function uses `shiny::NS()` to namespace all input IDs.
#'
#' @seealso
#' - [`mod_var_input_server()`] for server-side logic.
#' - [`shiny::tagList()`](https://shiny.rstudio.com/reference/shiny/latest/tagList.html)
#' for containing UI elements.
#' - [`shiny::NS()`](https://shiny.rstudio.com/reference/shiny/latest/NS.html)
#' for namespacing.
#'
#' @family **Variable Input Module**
#'
#' @examples
#'
#' if (interactive()) {
#' shiny::shinyApp(
#' ui = shiny::fluidPage(mod_var_input_ui("vars")),
#' server = function(input, output, session) {
#' selected_vars <- mod_var_input_server("vars")
#' shiny::observe(
#' print(selected_vars())
#' )
#' }
#' )
#' }
#'
```
**`mod_var_input_server()`**:
```{r}
#| eval: false
#| code-fold: show
#| code-summary: 'show/hide mod_var_input_server documentation'
#' Variable Input Module - Server
#'
#' Handles the server-side logic for the variable input module.
#'
#' @param id *(character)* Namespace ID for the module.
#'
#' @return A **reactive expression** that returns a list of selected input
#' values:
#' - `y`: Variable for the y-axis.
#' - `x`: Variable for the x-axis.
#' - `z`: Variable for the color aesthetic.
#' - `alpha`: Transparency level of points (0-1).
#' - `size`: Size of the points.
#' - `plot_title`: Title for the scatter plot.
#'
#' @section Details:
#' `mod_var_input_server()` reads user input from the corresponding UI
#' function created with
#' `mod_var_input_ui()`. It processes and returns a reactive object
#' containing the selected variables and plot attributes.
#'
#' @seealso
#' - [`mod_var_input_ui()`] for the UI counterpart of this module.
#' - [`mod_scatter_display_server()`] for displaying the scatter plot.
#'
#' @family **Variable Input Module**
#'
#' if (interactive()) {
#' shiny::shinyApp(
#' ui = shiny::fluidPage(mod_var_input_ui("vars")),
#' server = function(input, output, session) {
#' selected_vars <- mod_var_input_server("vars")
#' shiny::observe(print(selected_vars()))
#' }
#' )
#' }
#'
```
#### Graph display
**`mod_scatter_display_ui`**:
```{r}
#| eval: false
#| code-fold: show
#| code-summary: 'show/hide mod_scatter_display_ui documentation'
#' Scatter Plot Display Module - UI
#'
#' Creates a UI for displaying a scatter plot with user-selected options.
#'
#' @param id *(character)* Namespace ID for the module.
#'
#' @return A `shiny::tagList` containing the plot output and metadata.
#'
#' @section Details:
#' `mod_scatter_display_ui()` includes:
#' - A **scatter plot** created dynamically based on user input.
#' - Metadata about the app, including the data source.
#'
#' @seealso
#' - [`mod_scatter_display_server()`] for the server-side logic of this
#' module.
#' - [`scatter_plot()`] for the utility function generating the plot.
#'
#' @family **Plot Display Module**
#'
#' @examples
#' if (interactive()) {
#' shiny::shinyApp(
#' ui = shiny::fluidPage(mod_scatter_display_ui("plot")),
#' server = function(input, output, session) {
#' # Example usage - server logic is minimal for standalone testing.
#' }
#' )
#' }
#'
```
**`mod_scatter_display_server`**:
```{r}
#| eval: false
#| code-fold: show
#| code-summary: 'show/hide mod_scatter_display_server documentation'
#' Scatter Plot Display Module - Server
#'
#' Handles the server-side logic for rendering a scatter plot.
#'
#' @param id *(character)* Namespace ID for the module.
#' @param var_inputs *(reactive)* A reactive expression containing
#' user-selected variables and attributes.
#'
#' @return No direct return value. This function generates a plot output.
#'
#' @section Details:
#' `mod_scatter_display_server()`:
#' - Uses `var_inputs` to dynamically generate a scatter plot with
#' user-selected variables.
#' - Reads from the `movies` dataset, which must be loaded in the app
#' environment.
#' - Processes plot titles and axis labels to improve readability.
#'
#' @section Reactive Inputs:
#' - `var_inputs()$x`: X-axis variable.
#' - `var_inputs()$y`: Y-axis variable.
#' - `var_inputs()$z`: Color aesthetic variable.
#' - `var_inputs()$alpha`: Transparency level.
#' - `var_inputs()$size`: Size of points.
#' - `var_inputs()$plot_title`: Title of the plot.
#'
#' @seealso
#' - [`mod_var_input_server()`] for variable selection.
#' - [`scatter_plot()`] for generating the scatter plot.
#'
#' @family **Plot Display Module**
#'
#' @examples
#' if (interactive()) {
#' shiny::shinyApp(
#' ui = shiny::fluidPage(
#' mod_var_input_ui("vars"),
#' mod_scatter_display_ui("plot")
#' ),
#' server = function(input, output, session) {
#' selected_vars <- mod_var_input_server("vars")
#' mod_scatter_display_server("plot", selected_vars)
#' }
#' )
#' }
#'
```
#### Utility function
`scatter_plot()`:
```{r}
#| eval: false
#| code-fold: show
#| code-summary: 'show/hide scatter_plot() documentation'
#' Create scatter plot
#'
#' Custom [`ggplot2`](https://ggplot2.tidyverse.org/) function for building
#' scatter plots in `sap`.
#'
#'
#' @param df `data.frame` or `tibble`
#' @param x_var string variable mapped to `x` axis
#' @param y_var string variable mapped to `y` axis
#' Generate a Scatter Plot
#'
#' Creates a scatter plot using `ggplot2` with the specified data and
#' aesthetics.
#'
#' @param df *(data.frame)* The dataset containing the variables to plot.
#' @param x_var *(character)* Name of the variable for the x-axis.
#' @param y_var *(character)* Name of the variable for the y-axis.
#' @param col_var *(character)* Name of the variable for the color aesthetic.
#' @param alpha_var *(numeric)* Transparency level of points (0 to 1).
#' @param size_var *(numeric)* Size of points.
#'
#' @return A `ggplot` object representing the scatter plot.
#'
#' @section Details:
#' `scatter_plot()` is designed for use in Shiny applications but can also be
#' used independently.
#' It supports customization of transparency, size, and color aesthetics.
#'
#' @seealso
#' - [`mod_scatter_display_server()`] for integrating this function into the
#' scatter plot module.
#' - [`ggplot2::ggplot()`](https://ggplot2.tidyverse.org/) for details on
#' `ggplot2` usage.
#'
#' @family **Utility Functions**
#'
#' @examples
#' scatter_plot(
#' df = mtcars,
#' x_var = "mpg",
#' y_var = "hp",
#' col_var = "cyl",
#' alpha_var = 0.7,
#' size_var = 3
#' )
#'
```
## UI & Server functions {#sec-roxygen2-ui-server}
Splitting the standalone app function in `app.R` into separate UI and server functions has multiple benefits:
1. Documenting each function will be easier if they are stored in the `R/` folder.
2. Having dedicated UI, server, and app function means we can develop and test them independently.
3. A standalone app function can have multiple applications in the same app-package.
I’ve split the UI and server from `launch_app()` into separate `movies_ui()` and `movies_server()` below.
The `movies_ui()` doesn't have any arguments--it contains the theme, image, and application title:
```{r}
#| eval: false
#| code-fold: show
#| code-summary: 'show/hide movies_ui documentation'
#' User Interface for the Movies Review Application
#'
#' Creates the user interface (UI) for the Movies Review application, which
#' allows users to create customizable scatter plots based on movie data.
#'
#' @return A Shiny `tagList` object containing the UI elements.
#'
#' @section Details:
#' The interface is built using:
#' - **Theme**: `shinythemes::shinytheme("spacelab")`.
#' - **Title Panel**: Displays the app title and a logo.
#' - **Sidebar Panel**: Includes the variable selection module
#' ([`mod_var_input_ui`]).
#' - **Main Panel**: Displays the scatter plot module
#' ([`mod_scatter_display_ui`]).
#'
#' @seealso
#' - [`movies_server()`] for the server logic of the app.
#' - [`mod_var_input_ui()`] and [`mod_scatter_display_ui()`] for the modules
#' included in the UI.
#'
#' @family **Application Components**
#'
#' @examples
#' if (interactive()) {
#' shiny::shinyApp(ui = movies_ui(), server = movies_server)
#' }
#'
movies_ui <- function() {
tagList(
fluidPage(
theme = shinythemes::shinytheme("spacelab"),
titlePanel(
div(
img(
src = "shiny.png",
height = 60,
width = 55,
style = "margin:10px 10px"
),
"Movie Reviews"
)
),
sidebarLayout(
sidebarPanel(