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

Implement MetricTable module #290

Merged
merged 6 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ Suggests:
devtools,
here,
shinytest2,
stringr,
testthat (>= 3.0.0),
usethis,
withr
Remotes:
gsm=Gilead-BioStats/gsm@v2.1.0
gsm=Gilead-BioStats/gsm@dev
Config/testthat/edition: 3
Encoding: UTF-8
Language: en-US
Expand Down
36 changes: 0 additions & 36 deletions R/htmlDependency.R
Original file line number Diff line number Diff line change
Expand Up @@ -32,42 +32,6 @@ htmlDependency_Stylesheet <- function(
)
}

#' HighlightTableRow JavaScript
#'
#' Attach `highlightTableRow.js` to an app or other HTML exactly once.
#'
#' @returns An `html_dependency` object (see [htmltools::htmlDependency()]),
#' which is attached to the Shiny app exactly once, regardless how many times
#' it is added.
#' @keywords internal
htmlDependency_HighlightTableRow <- function() {
htmltools::htmlDependency(
name = "HighlightTableRow",
version = "1.0.0",
src = "js",
package = "gsm.app",
script = "highlightTableRow.js"
)
}

#' TableClick JavaScript
#'
#' Attach `tableClick.js` to an app or other HTML exactly once.
#'
#' @returns An `html_dependency` object (see [htmltools::htmlDependency()]),
#' which is attached to the Shiny app exactly once, regardless how many times
#' it is added.
#' @keywords internal
htmlDependency_TableClick <- function() {
htmltools::htmlDependency(
name = "TableClick",
version = "1.0.0",
src = "js",
package = "gsm.app",
script = "tableClick.js"
)
}

#' DetectCardClicks JavaScript
#'
#' Attach `detectCardClicks.js` to an app or other HTML exactly once.
Expand Down
38 changes: 7 additions & 31 deletions R/mod_MetricDetails_Server.R
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,6 @@ mod_MetricDetails_Server <- function(
}) %>%
bindCache(rctv_strMetricID())

rctv_dfResults_AnalysisOutput <- reactive({
rctv_dfResults_Latest() %>%
dplyr::arrange("GroupID") %>%
dplyr::select(
"GroupID", "Numerator", "Denominator", "Metric",
"Score", "Flag", "MetricID"
)
}) %>%
bindCache(rctv_strMetricID())

rctv_dfBounds_byMetricID <- reactive({
filter_byMetricID(dfBounds, rctv_strMetricID())
}) %>%
Expand All @@ -53,7 +43,12 @@ mod_MetricDetails_Server <- function(
rctv_strBarValueGroup <- reactive(NULL)
rctv_strBarScoreGroup <- reactive(NULL)
rctv_strTimeSeriesGroup <- reactive(NULL)
rctv_strAnalysisOutputGroup <- reactive(NULL)
rctv_strAnalysisOutputGroup <- mod_MetricTable_Server(
"analysis_output",
rctv_dfResults = rctv_dfResults_byMetricID,
dfGroups = dfGroups,
rctv_strSiteID = rctv_strSiteID
)

# Outputs ----
rctv_strSelectedGroupID <- reactive({
Expand Down Expand Up @@ -102,26 +97,7 @@ mod_MetricDetails_Server <- function(
outputOptions(output, "time_series", suspendWhenHidden = FALSE)
rctv_strTimeSeriesGroup()
},
"Analysis Output" = {
output$results <- renderUI({
gsm::Report_MetricTable(
rctv_dfResults_AnalysisOutput(),
dfGroups,
strGroupLevel = "Site"
) %>%
HTML()
})
observe({
shinyjs::runjs(
sprintf(
"highlightTableRow('analysis_output_table', '%s');",
rctv_strSiteID()
)
)
})
shinyjs::runjs("tableClick('analysis_output_table');")
rctv_strAnalysisOutputGroup()
}
"Analysis Output" = rctv_strAnalysisOutputGroup()
)
})

Expand Down
10 changes: 1 addition & 9 deletions R/mod_MetricDetails_UI.R
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,7 @@ mod_MetricDetails_UI <- function(id) {
),
bslib::nav_panel(
"Analysis Output",
div(
id = "analysis_output_table",
class = "card mb-3",
style = "margin-top: 4px;",
div(
class = "card-body",
uiOutput(ns("results"))
)
)
mod_MetricTable_UI(ns("analysis_output"))
)
)
}
72 changes: 72 additions & 0 deletions R/mod_MetricTable_Server.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#' Metric Table Module Server
#'
#' @inheritParams shared-params
#' @returns A [shiny::reactive()] with the id of the selected group.
#' @keywords internal
mod_MetricTable_Server <- function(
id,
rctv_dfResults,
dfGroups,
rctv_strSiteID
) {
moduleServer(id, function(input, output, session) {
output$table <- gt::render_gt({
req(rctv_dfResults())
tbl <- gsm::Report_MetricTable(
rctv_dfResults(),
dfGroups = dfGroups,
strGroupLevel = "Site"
)
# Hack to fix `Enrolled` sorting. See
# https://github.com/Gilead-BioStats/gsm/issues/1895
tbl$`_data`$Enrolled <- as.integer(tbl$`_data`$Enrolled)

tbl %>%
gt::opt_interactive(
use_resizers = TRUE,
use_highlight = TRUE,
use_compact_mode = TRUE,
use_text_wrapping = FALSE,
use_page_size_select = TRUE
) %>%
gt::tab_options(
table.background.color = "transparent",
column_labels.background.color = "transparent"
) %>%
gt::opt_row_striping()
})

observe({
session$sendCustomMessage(
"gtSetSelectID",
list(
id = session$ns("table"),
selectID = rctv_strSiteID()
)
)
})

# Reactive value to store the selected row
selected_row <- reactiveVal("None")

# Whenever the table changes, re-bind click events.
observeEvent(rctv_dfResults(), {
session$sendCustomMessage("gtBindClick", list(id = session$ns("table")))
})

# Observe table selections
observe({
req(rctv_dfResults())
if (length(input$table) > 0) {
selected_row(input$table)
} else {
selected_row("None")
}
})

# Return the selected row data
return(reactive({
selected_row()
}))
})
}
84 changes: 84 additions & 0 deletions R/mod_MetricTable_UI.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#' Metric Table Module UI
#'
#' @inheritParams shared-params
#' @returns A [bslib::card()] with an optional title and a
#' [gsm::Report_MetricTable()].
#' @keywords internal
mod_MetricTable_UI <- function(id) {
ns <- NS(id)
bslib::card(
id = id,
full_screen = TRUE,
class = "MetricTable",
gtIO(ns("table"))
)
}

#' gt Table Input and Output
#'
#' @param id
#'
#' @return An [htmltools::tagList()] containing the dependencies needed to use
#' gt as both an input and an output, and a [shiny::htmlOutput()] with class
#' "gtIO".
#' @keywords internal
gtIO <- function(id) {
htmltools::tagList(
htmlDependency_gtIO(),
shiny::htmlOutput(id, class = "gtIO")
)
}

#' gt Input-Output Dependencies
#'
#' Attach CSS and JavaScript necessary for gtIO to an app or other HTML exactly
#' once.
#'
#' @returns An [htmltools::tagList()] of `html_dependency` objects (see
#' [htmltools::htmlDependency()]), so that each will be attached to the Shiny
#' app exactly once, regardless how many times they are added.
#' @keywords internal
htmlDependency_gtIO <- function() {
htmltools::tagList(
htmlDependency_gtIOjs(),
htmlDependency_gtIOInput(),
htmlDependency_Stylesheet("gtIOStyle.css")
)
}

#' gt JavaScript
#'
#' Attach `gtIO.js` to an app or other HTML exactly once.
#'
#' @returns An `html_dependency` object (see [htmltools::htmlDependency()]),
#' which is attached to the Shiny app exactly once, regardless how many times
#' it is added.
#' @keywords internal
htmlDependency_gtIOjs <- function() {
htmltools::htmlDependency(
name = "gtIO",
version = "0.0.1",
src = "js",
package = "gsm.app",
script = "gtIO.js"
)
}


#' gt Input JavaScript
#'
#' Attach `gtIOInput.js` to an app or other HTML exactly once.
#'
#' @returns An `html_dependency` object (see [htmltools::htmlDependency()]),
#' which is attached to the Shiny app exactly once, regardless how many times
#' it is added.
#' @keywords internal
htmlDependency_gtIOInput <- function() {
htmltools::htmlDependency(
name = "gtShinyInput",
version = "0.0.1",
src = "inputs",
package = "gsm.app",
script = "gtIOInput.js"
)
}
2 changes: 0 additions & 2 deletions R/out_Sidebar.R
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ out_Sidebar <- function(
tagListSidebar,
shinyjs::useShinyjs(),
htmlDependency_Default_Stylesheet(),
htmlDependency_HighlightTableRow(),
htmlDependency_TableClick(),
out_StudyInformation(lStudy = lStudy),
out_Inputs(
chrMetrics = chrMetrics,
Expand Down
4 changes: 2 additions & 2 deletions inst/WORDLIST
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ Analytics
CMD
DetectCardClicks
GroupID
HighlightTableRow
KRI
MetricID
MetricIDs
Expand All @@ -13,7 +12,6 @@ Param
Rmd
ScatterPlot
ScatterPlotSet
TableClick
UI
chrWords
df
Expand All @@ -24,7 +22,9 @@ dfMetrics
dfResults
gsm
gsmApp
gtIO
io
lMetric
magrittr
namespaced
pkgdown
Expand Down
59 changes: 59 additions & 0 deletions inst/inputs/gtIOInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Shiny InputBinding for a gtIO Module.
*/
var gtIOBinding = new Shiny.InputBinding();

$.extend(gtIOBinding, {
/**
* Finds the gtIO element within the given scope.
*
* @param {HTMLElement} scope - The scope in which to search for the gtIO
* element.
* @returns {jQuery} The jQuery object containing the gtIO element.
*/
find: function(scope) {
return $(scope).find('.gtIO');
},
/**
* Gets the value of the selected select ID from the gtIO element.
*
* @param {HTMLElement} el - The element containing the gtIO.
* @returns {string|null} The selected select ID, or null if no select is
* selected.
*/
getValue: function(el) {
return $(el).data('selectID');
},
/**
* Sets the selected select ID for the gtIO element.
*
* @param {HTMLElement} el - The element containing the gtIO.
* @param {string} value - The new selected select ID.
*/
setValue: function(el, value) {
$(el).data('selectID', value);
},
/**
* Update Shiny when a gtIO element changes.
*
* @param {HTMLElement} el - The element containing the gtIO.
* @param {function} callback - The callback to trigger when the gtIO
* element changes.
*/
subscribe: function(el, callback) {
$(el).on('gtIO-value-changed', function(event) {
callback();
});
},
/**
* Unsubscribes from custom events for the gtIO element.
*
* @param {HTMLElement} el - The element containing the gtIO.
*/
unsubscribe: function(el) {
$(el).off('gtIO-value-changed');
}
});

// Register the input binding with Shiny
Shiny.inputBindings.register(gtIOBinding, 'gsm.app.gtIOBinding');
Loading
Loading