Skip to content

Commit bc1e21c

Browse files
Merge pull request #114 from Merck/113-toggle-button-to-showhide-risk-difference-column
add new feature for toggle risk difference columns
2 parents 4ed62d1 + 29adf76 commit bc1e21c

12 files changed

+237
-45
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,4 @@ Suggests:
5050
VignetteBuilder: knitr
5151
Config/testthat/edition: 3
5252
Roxygen: list(markdown = TRUE)
53-
RoxygenNote: 7.3.2
53+
RoxygenNote: 7.3.3

R/ae_forestly.R

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#'
2121
#' @param outdata An `outdata` object created by [format_ae_forestly()].
2222
#' @param display_soc_toggle A boolean value to display SOC toggle button.
23+
#' @param display_diff_toggle A boolean value to display risk difference toggle button.
2324
#' @param filter A character value of the filter variable.
2425
#' @param filter_label A character value of the label for slider bar.
2526
#' @param filter_range A numeric vector of length 2 for the range of the slider bar.
@@ -48,6 +49,7 @@
4849
#' }
4950
ae_forestly <- function(outdata,
5051
display_soc_toggle = TRUE,
52+
display_diff_toggle = FALSE,
5153
filter = c("prop", "n"),
5254
filter_label = NULL,
5355
filter_range = NULL,
@@ -186,12 +188,29 @@ ae_forestly <- function(outdata,
186188
filter_subject$children[[2]]$attribs$`data-to` <- filter_range[2]
187189
filter_subject$children[[2]]$attribs$`data-max` <- filter_range[2]
188190

191+
diff_cols <- c(
192+
names(outdata$diff)
193+
)
194+
195+
all_diff_cols <- c(diff_cols, "diff_fig")
196+
displayed_diff_cols <- intersect(all_diff_cols, c(
197+
if ("diff" %in% outdata$display) diff_cols else NULL,
198+
if ("fig_diff" %in% outdata$display) "diff_fig" else NULL
199+
))
200+
201+
hidden_cols <- outdata$hidden_column
202+
if (display_diff_toggle) {
203+
hidden_cols <- setdiff(hidden_cols, displayed_diff_cols)
204+
}
205+
189206
p_reactable <- reactable2(
190207
tbl,
191208
columns = outdata$reactable_columns,
192209
columnGroups = outdata$reactable_columns_group,
193-
hidden_item = paste0("'", outdata$hidden_column, "'", collapse = ", "),
210+
hidden_item = paste0("'", hidden_cols, "'", collapse = ", "),
194211
soc_toggle = display_soc_toggle,
212+
diff_toggle = display_diff_toggle,
213+
diff_columns = displayed_diff_cols,
195214
width = width,
196215
download = dowload_button,
197216
searchable = FALSE,

R/format_ae_forestly.R

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@
3838
#' for risk difference figure.
3939
#' @param color A vector of colors for analysis groups.
4040
#' Default value supports up to 4 groups.
41+
#' @param ae_col_header Column header for adverse events item columns.
42+
#' If NULL (default) and "par" specified in `components` from `prepare_ae_forestly()`, uses "Adverse Event".
43+
#' If NULL and "soc" specified in `components` from `prepare_ae_forestly()`, uses "System Organ Class" for "soc".
4144
#' @param diff_label x-axis label for risk difference.
42-
#' @param col_header Column header for risk difference table columns.
45+
#' @param diff_col_header Column header for risk difference table columns.
4346
#' If NULL (default), uses "Risk Difference (%) <br> vs. Reference Group".
44-
#' @param fig_header Column header for risk difference figure.
47+
#' @param diff_fig_header Column header for risk difference figure.
4548
#' If NULL (default), uses "Risk Difference (%) + 95% CI <br> vs. Reference Group".
46-
#' @param show_ae_parameter A boolean value to display AE parameter column.
4749
#'
4850
#' @return An `outdata` object.
4951
#'
@@ -71,10 +73,10 @@ format_ae_forestly <- function(
7173
prop_range = NULL,
7274
diff_range = NULL,
7375
color = NULL,
76+
ae_col_header = NULL,
7477
diff_label = "Treatment <- Favor -> Placebo",
75-
col_header = NULL,
76-
fig_header = NULL,
77-
show_ae_parameter = FALSE) {
78+
diff_col_header = NULL,
79+
diff_fig_header = NULL) {
7880
display <- tolower(display)
7981

8082
display <- match.arg(
@@ -105,12 +107,20 @@ format_ae_forestly <- function(
105107
reference_name <- outdata$group[index_reference]
106108

107109
# Set default headers if not provided
108-
if (is.null(col_header)) {
109-
col_header <- paste0("Risk Difference (%) <br> vs. ", reference_name)
110+
if (is.null(ae_col_header)) {
111+
if ("par" %in% outdata$components) {
112+
ae_col_header <- "Adverse Event"
113+
} else if ("soc" %in% outdata$components) {
114+
ae_col_header <- "System Organ Class"
115+
}
116+
}
117+
118+
if (is.null(diff_col_header)) {
119+
diff_col_header <- paste0("Risk Difference (%) <br> vs. ", reference_name)
110120
}
111121

112-
if (is.null(fig_header)) {
113-
fig_header <- paste0("Risk Difference (%) + 95% CI <br> vs. ", reference_name)
122+
if (is.null(diff_fig_header)) {
123+
diff_fig_header <- paste0("Risk Difference (%) + 95% CI <br> vs. ", reference_name)
114124
}
115125

116126
# Input checking
@@ -143,8 +153,6 @@ format_ae_forestly <- function(
143153
hide_n = apply(outdata$n[, 1:n_group], 1, max, na.rm = TRUE)
144154
)
145155

146-
if (!show_ae_parameter) tbl <- tbl[, c(2:ncol(tbl), 1)]
147-
148156
rownames(tbl) <- NULL
149157

150158
# JavaScript for plotly figures ----
@@ -251,7 +259,7 @@ format_ae_forestly <- function(
251259
)
252260
}
253261
columnGroups[[m_group + 1]] <- reactable::colGroup(
254-
name = col_header,
262+
name = diff_col_header,
255263
html = TRUE,
256264
columns = names(outdata$diff)
257265
)
@@ -262,10 +270,10 @@ format_ae_forestly <- function(
262270
col_var <- list(
263271
parameter = reactable::colDef(
264272
header = "Type",
265-
show = show_ae_parameter
273+
show = FALSE
266274
),
267275
name = reactable::colDef(
268-
header = "Adverse Events",
276+
header = ae_col_header,
269277
minWidth = width_term, align = "right"
270278
),
271279
soc_name = reactable::colDef(
@@ -342,7 +350,7 @@ format_ae_forestly <- function(
342350

343351
# difference format
344352
col_diff_fig <- list(diff_fig = reactable::colDef(
345-
header = fig_header,
353+
header = diff_fig_header,
346354
defaultSortOrder = "desc",
347355
width = ifelse("fig_diff" %in% display, width_fig, 0),
348356
align = "center",

R/prepare_ae_forestly.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ prepare_ae_forestly <- function(
170170
order = info$order,
171171
parameter_order = parameter_order,
172172
group = res[[1]]$group,
173+
components = components,
173174
reference_group = res[[1]]$reference_group,
174175
prop = values$prop,
175176
diff = values$diff,

R/reactable2.R

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@
4848
#' @param label A logical value to display label as a hover text.
4949
#' @param download A logical value to display download button.
5050
#' @param soc_toggle A logical value to display SOC toggle button.
51+
#' @param diff_toggle A logical value to display risk difference toggle button.
5152
#' @param hidden_item Vector for hidden columns.
53+
#' @param diff_columns Character vector of risk difference column names.
5254
#' @param ... Additional arguments passed to [reactable::reactable()].
5355
#' @inheritParams reactable::reactable
5456
#'
@@ -76,7 +78,9 @@ reactable2 <- function(
7678
download = TRUE,
7779
col_def = NULL,
7880
soc_toggle = TRUE,
81+
diff_toggle = FALSE,
7982
hidden_item = NULL,
83+
diff_columns = NULL,
8084
...) {
8185
# Display variable label as hover text
8286
if (label & is.null(col_def)) {
@@ -110,25 +114,60 @@ reactable2 <- function(
110114
...
111115
)
112116

117+
buttons <- list()
118+
113119
if (soc_toggle) {
114-
on_click2 <- paste0(
115-
"function control_column(hidden_columns) {",
120+
on_click_soc <- paste0(
121+
"function control_soc(hidden_columns) {",
116122
" if (hidden_columns.includes('soc_name')) {",
117123
" Reactable.setHiddenColumns('", element_id, "', prevColumns => {
118-
return prevColumns.length === 0 ? ['soc_name']:[", hidden_item, "]})",
124+
return prevColumns.filter(col => col !== 'soc_name')})",
119125
" } else {",
120126
" Reactable.setHiddenColumns('", element_id, "', prevColumns => {
121-
return prevColumns.length === 0 ? [ ]: ['soc_name',", hidden_item, "]})",
127+
return [...prevColumns, 'soc_name']})",
122128
" }",
123129
"}",
124-
"control_column(Reactable.getState('", element_id, "').hiddenColumns);"
130+
"control_soc(Reactable.getState('", element_id, "').hiddenColumns);"
125131
)
126132

127-
tbl <- htmltools::tagList(
133+
buttons <- c(buttons, list(
128134
htmltools::tags$button(
129135
"Show/Hide SOC column",
130-
onclick = on_click2
131-
),
136+
onclick = on_click_soc
137+
)
138+
))
139+
}
140+
141+
if (diff_toggle && !is.null(diff_columns) && length(diff_columns) > 0) {
142+
diff_cols_js <- paste0("['", paste(diff_columns, collapse = "', '"), "']")
143+
on_click_diff <- paste0(
144+
"function control_diff(hidden_columns) {",
145+
" const diffCols = ", diff_cols_js, ";",
146+
" const allDiffHidden = diffCols.every(col => hidden_columns.includes(col));",
147+
" if (allDiffHidden) {",
148+
" Reactable.setHiddenColumns('", element_id, "', prevColumns => {
149+
return prevColumns.filter(col => !diffCols.includes(col))})",
150+
" } else {",
151+
" Reactable.setHiddenColumns('", element_id, "', prevColumns => {
152+
return [...new Set([...prevColumns, ...diffCols])]})",
153+
" }",
154+
"}",
155+
"control_diff(Reactable.getState('", element_id, "').hiddenColumns);"
156+
)
157+
158+
buttons <- c(buttons, list(
159+
htmltools::tags$button(
160+
"Show/Hide Risk Difference",
161+
onclick = on_click_diff
162+
)
163+
))
164+
}
165+
166+
167+
168+
if (length(buttons) > 0) {
169+
tbl <- htmltools::tagList(
170+
buttons,
132171
tbl
133172
)
134173
}

_pkgdown.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ articles:
3737
- customize-color
3838
- customize-ae-specific-columns
3939
- customize-diff-label
40+
- customize-toggle-buttons
4041
- customize-xlimit
4142
- customize-width
4243
- customize-display-only-soc

man/ae_forestly.Rd

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/format_ae_forestly.Rd

Lines changed: 9 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-ae_forestly.R

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,34 @@ test_that("ae_forestly(): test filter and width option", {
1717
expect_true(grepl("width:1500px", html$children[[1]], fixed = TRUE))
1818
expect_true(grepl("Number of AE in One or More Treatment Groups", html$children[[1]], fixed = TRUE))
1919
})
20+
21+
test_that("ae_forestly(): toggle risk difference button is hidden by default", {
22+
outdata <- metalite.ae::meta_ae_example() |>
23+
prepare_ae_forestly(
24+
population = "apat",
25+
observation = "wk12",
26+
parameter = "any;rel;ser"
27+
) |>
28+
format_ae_forestly(display = c("n", "prop", "fig_prop", "fig_diff", "diff"))
29+
30+
html <- outdata |> ae_forestly(display_diff_toggle = FALSE)
31+
html_text <- as.character(html)
32+
33+
expect_false(grepl("Show/Hide Risk Difference", html_text, fixed = TRUE))
34+
})
35+
36+
test_that("ae_forestly(): toggle risk difference button can be enabled", {
37+
outdata <- metalite.ae::meta_ae_example() |>
38+
prepare_ae_forestly(
39+
population = "apat",
40+
observation = "wk12",
41+
parameter = "any;rel;ser"
42+
) |>
43+
format_ae_forestly(display = c("n", "prop", "fig_prop", "fig_diff", "diff"))
44+
45+
html <- outdata |> ae_forestly(display_diff_toggle = TRUE)
46+
html_text <- as.character(html)
47+
48+
expect_true(grepl("Show/Hide Risk Difference", html_text, fixed = TRUE))
49+
expect_true(grepl("control_diff", html_text, fixed = TRUE))
50+
})

0 commit comments

Comments
 (0)