From 78ae421127b7e004eb074e0951a8ec4bc4f965ea Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Mon, 16 Feb 2026 10:15:09 -0500 Subject: [PATCH 01/21] Update a11y guide with info about how to escape special characters in alt text/captions csv --- vignettes/accessibility_guide.Rmd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vignettes/accessibility_guide.Rmd b/vignettes/accessibility_guide.Rmd index 592d5f1a..ffe5a9cf 100644 --- a/vignettes/accessibility_guide.Rmd +++ b/vignettes/accessibility_guide.Rmd @@ -185,6 +185,8 @@ recruitment_alt_text <- new_alt_text 3. As stated earlier, if you see text that looks like a placeholder (e.g., "The x axis, showing the year, spans from B.start.year to B.end.year..."), that means that there was at least one instance where our tool failed to extract a specific value from the model results and substitute it into the placeholder. Please make sure that your alt text and captions contain the expected values before moving forward with your report. Check out the inst/resources/captions_alt_text_template.csv file in the `stockplotr` package to view the template with placeholders. The same package's `write_captions()` function shows how values are extracted from the model results and substituted into the placeholders. +4. If you add a special character (e.g., percentage sign (%) or dollar sign ($); see [full list on this wiki page](https://en.wikibooks.org/wiki/LaTeX/Special_Characters#Other_symbols)), please add two backslashes before the character to avoid issues compiling your report later on (specifically, the conversion from Quarto to LaTeX via Pandoc, and then compilation of the LaTeX report after running `add_accessibility()` or `add_alttext()`). For example, "The 95% CI" would be written as "The 95\\\\% CI". + ### More resources Looking for more resources for writing alt text? Check out the [NOAA Library's website for creating accessible documents](https://library.noaa.gov/Section508/CreatingDocs). From 3cf3a0f14604d686bb32cfe5033cebadec22cd76 Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Mon, 16 Feb 2026 10:19:33 -0500 Subject: [PATCH 02/21] Update readme example create_template() arg from 'author' to 'authors' --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5877e2ec..4917d24e 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ asar::create_template( species = "Petrale sole", spp_latin = "Eopsetta jordani", year = 2023, - author = c("Ian G. Taylor"="NWFSC", "Vladlena Gertseva"="NWFSC", "Nick Tolimieri"="NWFSC"), + authors = c("Ian G. Taylor"="NWFSC", "Vladlena Gertseva"="NWFSC", "Nick Tolimieri"="NWFSC"), include_affiliation = TRUE, simple_affiliation = FALSE, param_names = c("nf","sf"), From c600ab312f08cf70409d16e448f87230941a696f Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Mon, 16 Feb 2026 11:06:51 -0500 Subject: [PATCH 03/21] Update ID_tbl_width_class() to work with gt tables, vs flextables --- R/ID_tbl_width_class.R | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/R/ID_tbl_width_class.R b/R/ID_tbl_width_class.R index 07b6238d..b9449f20 100644 --- a/R/ID_tbl_width_class.R +++ b/R/ID_tbl_width_class.R @@ -34,9 +34,14 @@ ID_tbl_width_class <- function( load(tables_path) table_rda <- rda rm(rda) - table_width <- flextable::flextable_dim(table_rda$table)[["widths"]] |> - as.numeric() + gt_table <- table_rda$table + # Set each col to 1.5" + col_inches <- 1.5 + + # Calculate total table width in inches + table_width <- ncol(gt_table[["_data"]]) * col_inches + # determine table width class if (table_width <= portrait_pg_width) { width_class <- "regular" From 1326b9236fc94cb0a1ca7de825f350eb1c861ef5 Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Mon, 16 Feb 2026 16:46:25 -0500 Subject: [PATCH 04/21] Update create_tables_doc() and related functions so begin replacing flextable-based pipeline with gt (for identifying table width, and splitting/rotating where needed). Next step: split super-long tables where necessary --- R/create_tables_doc.R | 29 ++++++++---- R/export_split_tbls.R | 2 +- R/render_lg_table.R | 104 +++++++++++++++++------------------------ man/render_lg_table.Rd | 8 ++-- 4 files changed, 70 insertions(+), 73 deletions(-) diff --git a/R/create_tables_doc.R b/R/create_tables_doc.R index 9e78926d..8ec9398a 100644 --- a/R/create_tables_doc.R +++ b/R/create_tables_doc.R @@ -77,7 +77,7 @@ create_tables_doc <- function(subdir = getwd(), tables_doc_setup <- paste0( add_chunk( glue::glue( - "library(flextable) + "library(gt) tables_dir <- fs::path('{tables_dir}', 'tables')" ), label = "set-rda-dir-tbls", @@ -122,9 +122,9 @@ create_tables_doc <- function(subdir = getwd(), ) # identify table orientation - # split tables will always be extra_wide + # split tables will always be extra-wide tbl_orient <- ifelse(split, - "extra_wide", + "extra-wide", ID_tbl_width_class( plot_name = tab_shortname, tables_dir = tables_dir, @@ -175,8 +175,14 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n "::: {.landscape}\n\n", add_chunk( glue::glue( - "{tab_shortname}_table |> - flextable::fit_to_width(max_width = 8)" + "{tab_shortname}_table |>\n", + " gt::tab_options(\n", + " table.width = pct(100),\n", + " table.layout = 'auto'\n", + " ) |>\n", + " gt::cols_width(\n", + " everything() ~ px(144)\n", + " )" ), label = glue::glue("tbl-{tab_shortname}"), # add_option = TRUE, @@ -194,13 +200,13 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n ) } - if (tbl_orient == "extra_wide") { + if (tbl_orient == "extra-wide") { if (split) { # identify number of split tables load(fs::path(tables_dir, "tables", tab)) split_tables <- length(table_list) } else { - # split extra_wide tables into smaller tables and export AND + # split extra-wide tables into smaller tables and export AND # identify number of split tables IF not already split split_tables <- export_split_tbls( tables_dir = tables_dir, @@ -240,7 +246,14 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n add_chunk( paste0( "# plot split table ", i, "\n", - tab_shortname, "_table_split_rda[[", i, "]] |> flextable::fit_to_width(max_width = 8)\n" + tab_shortname, "_table_split_rda[[", i, "]] |>\n", + " gt::tab_options(\n", + " table.width = pct(100),\n", + " table.layout = 'auto'\n", + " ) |>\n", + " gt::cols_width(\n", + " everything() ~ px(144)\n", + " ) \n" ), label = glue::glue("tbl-{tab_shortname}", i), add_option = TRUE, diff --git a/R/export_split_tbls.R b/R/export_split_tbls.R index ad28b228..922b153d 100644 --- a/R/export_split_tbls.R +++ b/R/export_split_tbls.R @@ -32,7 +32,7 @@ export_split_tbls <- function( # split tables and export render_lg_table( - report_flextable = table_rda$table, + report_gt = table_rda$table, essential_columns = essential_columns, tables_dir = tables_dir, plot_name = plot_name diff --git a/R/render_lg_table.R b/R/render_lg_table.R index f1aa622a..a56c0d7c 100644 --- a/R/render_lg_table.R +++ b/R/render_lg_table.R @@ -1,6 +1,6 @@ #' Split an extra-wide table into multiple tables #' -#' @param report_flextable The extra-wide flextable. +#' @param report_gt The extra-wide gt table. #' @param essential_columns The columns that will be retained between the split #' tables, formatted as a sequence (e.g., 1:2 for columns 1-2, or 1 for a single #' column. Example: for the indices table, this could be the year column. @@ -14,37 +14,37 @@ #' @examples #' \dontrun{ #' render_lg_table( -#' report_flextable = indices_table, +#' report_gt = indices_table, #' essential_columns = 1, #' tables_dir = here::here(), #' plot_name = "indices.abundance_table.rda" #' ) #' #' render_lg_table( -#' report_flextable = important_table, +#' report_gt = important_table, #' essential_columns = 1:3, #' tables_dir = "data", #' plot_name = "bnc_table.rda" #' ) #' } -render_lg_table <- function(report_flextable, +render_lg_table <- function(report_gt, essential_columns, tables_dir, plot_name) { - # calculate key numbers - # total columns, width of table - total_cols <- flextable::ncol_keys(report_flextable) - total_width <- flextable::flextable_dim(report_flextable)[["widths"]] + # Set each col to 1.5" + col_inches <- 1.5 + + # calculate key numbers + # total columns, width of table in inches + total_cols <- ncol(report_gt[["_data"]]) + total_width <- total_cols * col_inches - # goal width of each table split from report_flextable, in inches + # goal width of each table split from report_gt, in inches goal_width <- 7.5 # max is 8" - # approx width of each column in report_flextable - approx_col_width <- total_width / total_cols - - # goal number of columns per table split from report_flextable - goal_cols_per_table <- ceiling(goal_width / approx_col_width) + # goal number of columns per table split from report_gt + goal_cols_per_table <- ceiling(goal_width / col_inches) # split tables needed num_tables <- ceiling(total_cols / goal_cols_per_table) @@ -117,70 +117,54 @@ render_lg_table <- function(report_flextable, }, cols_to_del_seq, essential_cols_seq) ) - # print all split tables by removing final_cols_to_del from report_flextable - # for (i in 1:num_tables) { - # report_flextable |> - # flextable::delete_columns(j = c( - # as.numeric( - # unlist( - # strsplit( - # table_cols[i,"final_cols_to_del"], ",") - # ) - # ) - # ) - # ) |> - # print() - # } - + # save all tables to a list table_list <- list() for (i in 1:num_tables) { - split_table <- report_flextable |> - flextable::delete_columns(j = c( - as.numeric( - unlist( - strsplit( - table_cols[i, "final_cols_to_del"], "," + split_table <- report_gt |> + gt::cols_hide( + columns = all_of( + as.numeric( + unlist( + strsplit( + table_cols[i, "final_cols_to_del"], "," + ) + ) ) ) - ) - )) |> - flextable::fit_to_width(max_width = goal_width) |> - flextable::hline( - part = "header", - # i = 1, - border = officer::fp_border( - width = 1.5, - color = "#666666" - ) + ) |> + gt::tab_options(table.width = px(goal_width * 96)) |> # in pixels + gt::tab_style( + style = cell_borders( + sides = "bottom", + color = "#666666", + weight = px(1.5) + ), + locations = cells_column_labels() ) |> - flextable::valign(valign = "center", part = "header") + gt::tab_style( + style = cell_text(v_align = "middle"), + locations = cells_column_labels() + ) table_list[[i]] <- split_table # get rownames of split table - all_vals <- split_table$header$dataset - shown_vals <- c(split_table[["header"]][["content"]][["keys"]]) + all_vals <- split_table[["_boxhead"]] split_tbl_vals <- all_vals |> - dplyr::select(dplyr::intersect( - names(all_vals), - shown_vals - )) |> - dplyr::slice(1) |> - dplyr::select_if(~ !(all(is.na(.)) | all(. == ""))) |> + dplyr::filter(type != "hidden") |> + # Pull the display labels (this handles cases where you renamed columns) + dplyr::pull(column_label) |> as.character() |> + # Replaces subset() - filter keeps only the values that match + (\(x) x[!is.na(x) & x != ""])() |> unique() |> stringr::str_squish() |> paste(collapse = ", ") # add rownames to table_list names(table_list)[[i]] <- split_tbl_vals - - - # if(i == num_tables){ - # single_tab <- table_list[[returned_tab]] - # return(single_tab) - # } + } # save table_list as rda save(table_list, diff --git a/man/render_lg_table.Rd b/man/render_lg_table.Rd index 8b4229e2..26b59a0e 100644 --- a/man/render_lg_table.Rd +++ b/man/render_lg_table.Rd @@ -4,10 +4,10 @@ \alias{render_lg_table} \title{Split an extra-wide table into multiple tables} \usage{ -render_lg_table(report_flextable, essential_columns, tables_dir, plot_name) +render_lg_table(report_gt, essential_columns, tables_dir, plot_name) } \arguments{ -\item{report_flextable}{The extra-wide flextable.} +\item{report_gt}{The extra-wide gt table.} \item{essential_columns}{The columns that will be retained between the split tables, formatted as a sequence (e.g., 1:2 for columns 1-2, or 1 for a single @@ -27,14 +27,14 @@ Split an extra-wide table into multiple tables \examples{ \dontrun{ render_lg_table( - report_flextable = indices_table, + report_gt = indices_table, essential_columns = 1, tables_dir = here::here(), plot_name = "indices.abundance_table.rda" ) render_lg_table( - report_flextable = important_table, + report_gt = important_table, essential_columns = 1:3, tables_dir = "data", plot_name = "bnc_table.rda" From 4e1a5a376fb90f6494fc2bd262d29514d6be5ad6 Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Tue, 17 Feb 2026 14:49:15 -0500 Subject: [PATCH 05/21] create new function to get table length; update documentation --- NAMESPACE | 1 + R/ID_tbl_length_class.R | 47 ++++++++++++++++++++++++++++++++++++++ R/ID_tbl_width_class.R | 2 +- man/ID_tbl_length_class.Rd | 30 ++++++++++++++++++++++++ man/ID_tbl_width_class.Rd | 2 +- 5 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 R/ID_tbl_length_class.R create mode 100644 man/ID_tbl_length_class.Rd diff --git a/NAMESPACE b/NAMESPACE index 6f069d1d..6315cdcf 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +export(ID_tbl_length_class) export(ID_tbl_width_class) export(add_accessibility) export(add_alttext) diff --git a/R/ID_tbl_length_class.R b/R/ID_tbl_length_class.R new file mode 100644 index 00000000..7c3066e7 --- /dev/null +++ b/R/ID_tbl_length_class.R @@ -0,0 +1,47 @@ +#' Identify table length class +#' +#' @inheritParams render_lg_table +#' +#' @return The length class of a table: regular or long. The result +#' will determine whether the table can be rendered on a page +#' as-is, or if it needs to be split across multiple pages. +#' @export +#' +#' @examples +#' \dontrun{ +#' ID_tbl_length_class( +#' plot_name = "indices.abundance_table.rda", +#' tables_dir = here::here() +#' ) +#' } +ID_tbl_length_class <- function( + tables_dir, + plot_name +) { + tables_path <- fs::path( + tables_dir, + "tables", + paste0(plot_name, "_table.rda") + ) + + if (file.exists(tables_path)) { + load(tables_path) + table_rda <- rda + rm(rda) + gt_table <- table_rda$table + + # Get table length in rows + table_length <- nrow(gt_table[["_data"]]) + + # determine table length class + if (table_length <= 30) { + length_class <- "regular" + } else { + length_class <- "long" + } + } else { + cli::cli_abort(message = "Table not found at {tables_path}") + } + + length_class +} diff --git a/R/ID_tbl_width_class.R b/R/ID_tbl_width_class.R index b9449f20..9e83d3c0 100644 --- a/R/ID_tbl_width_class.R +++ b/R/ID_tbl_width_class.R @@ -7,7 +7,7 @@ #' be resized, rotated, and/or split across multiple pages. #' #' @return The width class of a table: regular, wide, or extra-wide. The result -#' will determine whether the table that can be rendered on a portrait page +#' will determine whether the table can be rendered on a portrait page #' as-is, or if it needs to be resized, rotated, and/or split across multiple pages. #' @export #' diff --git a/man/ID_tbl_length_class.Rd b/man/ID_tbl_length_class.Rd new file mode 100644 index 00000000..8ff82d41 --- /dev/null +++ b/man/ID_tbl_length_class.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ID_tbl_length_class.R +\name{ID_tbl_length_class} +\alias{ID_tbl_length_class} +\title{Identify table length class} +\usage{ +ID_tbl_length_class(tables_dir, plot_name) +} +\arguments{ +\item{tables_dir}{The location of the "tables" folder, which contains tables +files.} + +\item{plot_name}{Name of the .rda file containing the table} +} +\value{ +The length class of a table: regular or long. The result +will determine whether the table can be rendered on a page +as-is, or if it needs to be split across multiple pages. +} +\description{ +Identify table length class +} +\examples{ +\dontrun{ +ID_tbl_length_class( + plot_name = "indices.abundance_table.rda", + tables_dir = here::here() +) +} +} diff --git a/man/ID_tbl_width_class.Rd b/man/ID_tbl_width_class.Rd index 35de7dd8..da234fdb 100644 --- a/man/ID_tbl_width_class.Rd +++ b/man/ID_tbl_width_class.Rd @@ -19,7 +19,7 @@ be resized, rotated, and/or split across multiple pages.} } \value{ The width class of a table: regular, wide, or extra-wide. The result -will determine whether the table that can be rendered on a portrait page +will determine whether the table can be rendered on a portrait page as-is, or if it needs to be resized, rotated, and/or split across multiple pages. } \description{ From f5906c9b39d7056d5501b32966acf76b80d8471e Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Mon, 23 Feb 2026 11:05:34 -0500 Subject: [PATCH 06/21] Redesign tables doc workflow to work with >1 tables shown on portrait orientation and 1 table shown on landscape orientation --- R/ID_tbl_length_class.R | 6 +- R/create_tables_doc.R | 172 +++++++++++++++++++++++++++++++--------- 2 files changed, 138 insertions(+), 40 deletions(-) diff --git a/R/ID_tbl_length_class.R b/R/ID_tbl_length_class.R index 7c3066e7..303cb08d 100644 --- a/R/ID_tbl_length_class.R +++ b/R/ID_tbl_length_class.R @@ -34,10 +34,10 @@ ID_tbl_length_class <- function( table_length <- nrow(gt_table[["_data"]]) # determine table length class - if (table_length <= 30) { - length_class <- "regular" + if (table_length <= 38) { + length_class <- "regular" # fit on one landscape page (and, by default, portrait) } else { - length_class <- "long" + length_class <- "long" # divided across >1 landscape page } } else { cli::cli_abort(message = "Table not found at {tables_path}") diff --git a/R/create_tables_doc.R b/R/create_tables_doc.R index 8ec9398a..874ad42c 100644 --- a/R/create_tables_doc.R +++ b/R/create_tables_doc.R @@ -131,6 +131,33 @@ create_tables_doc <- function(subdir = getwd(), portrait_pg_width = portrait_pg_width ) ) + + # identify table length: regular (1 landscape page) or long (>1 landscape page) + tbl_length <- ID_tbl_length_class( + plot_name = tab_shortname, + tables_dir = tables_dir + ) + + table_specs <- list(tbl_orient, tbl_length) + + tbl_class <- dplyr::case_when( + table_specs[[1]] == "regular" & table_specs[[2]] == "regular" ~ "reg_reg", # 38 rows / portrait + table_specs[[1]] == "regular" & table_specs[[2]] == "long" ~ "reg_long", # 38 rows, split / portrait + table_specs[[1]] == "wide" & table_specs[[2]] == "regular" ~ "wide_reg", # 28 rows / landscape + table_specs[[1]] == "wide" & table_specs[[2]] == "long" ~ "wide_long", # 28 rows, split / landscape + table_specs[[1]] == "extra-wide" & table_specs[[2]] == "regular" ~ "ewide_reg", # 28 rows, split / landscape + table_specs[[1]] == "extra-wide" & table_specs[[2]] == "long" ~ "ewide_long", # 28 rows, split / landscape + TRUE ~ "unknown" + ) + + if (tbl_class == "unknown"){ + cli::cli_abort("Unknown table class. Check table is an acceptable `gt` table.") + } + + # set max number of rows per table based on orientation + max_rows <- ifelse(tbl_orient == "regular", + 38, # max rows for portrait + 28) # max rows for landscape ## import table, caption ## do this for all tables @@ -150,8 +177,8 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n "\n" ) - ## add table if it only requires one chunk - if (tbl_orient == "regular") { + ## add table if it only requires one chunk, on a portrait page + if (tbl_class == "reg_reg") { tables_doc_plot_setup2 <- paste0( add_chunk( glue::glue("{tab_shortname}_table"), @@ -168,21 +195,22 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n "\n" ) } - - if (tbl_orient == "wide") { + + ## add table if it only requires one chunk, rotated on a landscape page + if (tbl_class == "wide_reg") { tables_doc_plot_setup2 <- paste0( # add landscape braces before R chunk "::: {.landscape}\n\n", add_chunk( glue::glue( "{tab_shortname}_table |>\n", - " gt::tab_options(\n", - " table.width = pct(100),\n", - " table.layout = 'auto'\n", - " ) |>\n", - " gt::cols_width(\n", - " everything() ~ px(144)\n", - " )" + " gt::tab_options(\n", + " table.width = pct(100),\n", + " table.layout = 'auto'\n", + " ) |>\n", + " gt::cols_width(\n", + " everything() ~ px(144)\n", + " )" ), label = glue::glue("tbl-{tab_shortname}"), # add_option = TRUE, @@ -200,29 +228,20 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n ) } - if (tbl_orient == "extra-wide") { - if (split) { + ## add table if it is split across portrait pages + if (tbl_class == "reg_long") { # identify number of split tables load(fs::path(tables_dir, "tables", tab)) split_tables <- length(table_list) - } else { - # split extra-wide tables into smaller tables and export AND - # identify number of split tables IF not already split - split_tables <- export_split_tbls( - tables_dir = tables_dir, - plot_name = tab, - essential_columns = 1 - ) - } - + # add a chunk to import split tables tables_doc_plot_setup2_import <- paste0( add_chunk( paste0( "load(file.path(tables_dir, '", tab, "'))\n -# save rda with plot-specific name\n", + # save rda with plot-specific name\n", tab_shortname, "_table_split_rda <- table_list\n -# extract table caption specifiers\n", + # extract table caption specifiers\n", tab_shortname, "_cap_split <- names(", tab_shortname, "_table_split_rda)" ), label = glue::glue("tbl-{tab_shortname}-labels"), @@ -241,19 +260,18 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n # add a chunk for each table tables_doc_plot_setup2_display <- paste0( tables_doc_plot_setup2_display, - # add landscape braces before R chunk - "::: {.landscape}\n\n", add_chunk( paste0( "# plot split table ", i, "\n", tab_shortname, "_table_split_rda[[", i, "]] |>\n", - " gt::tab_options(\n", - " table.width = pct(100),\n", - " table.layout = 'auto'\n", + " gt::tab_options(\n", + " table.width = pct(100),\n", + " table.layout = 'auto'\n", " ) |>\n", - " gt::cols_width(\n", - " everything() ~ px(144)\n", - " ) \n" + " gt::cols_width(\n", + " everything() ~ px(144) |>\n", + " gt::gt_split(row_every_n = ", max_length, ") |>\n", + " gt::grp_pull(", i, ")\n" ), label = glue::glue("tbl-{tab_shortname}", i), add_option = TRUE, @@ -264,17 +282,97 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n ) ) ), - "\n", - # add landscape braces after R chunk - ":::\n" + "\n" ) } - + tables_doc_plot_setup2 <- paste0( tables_doc_plot_setup2_import, tables_doc_plot_setup2_display ) } + + + + # ## add table if it is rotated and split across landscape pages ("wide_long", "ewide_reg", "ewide_long") + + + +# if (tbl_orient == "extra-wide") { +# if (split) { +# # identify number of split tables +# load(fs::path(tables_dir, "tables", tab)) +# split_tables <- length(table_list) +# } else { +# # split extra-wide tables into smaller tables and export AND +# # identify number of split tables IF not already split +# split_tables <- export_split_tbls( +# tables_dir = tables_dir, +# plot_name = tab, +# essential_columns = 1 +# ) +# } +# +# # add a chunk to import split tables +# tables_doc_plot_setup2_import <- paste0( +# add_chunk( +# paste0( +# "load(file.path(tables_dir, '", tab, "'))\n +# # save rda with plot-specific name\n", +# tab_shortname, "_table_split_rda <- table_list\n +# # extract table caption specifiers\n", +# tab_shortname, "_cap_split <- names(", tab_shortname, "_table_split_rda)" +# ), +# label = glue::glue("tbl-{tab_shortname}-labels"), +# # add_option = TRUE, +# chunk_option = c( +# "echo: false", +# "warnings: false", +# glue::glue("include: false") +# ) +# ), +# "\n" +# ) +# # prepare text for chunk that will display split tables +# tables_doc_plot_setup2_display <- "" +# for (i in 1:as.numeric(split_tables)) { +# # add a chunk for each table +# tables_doc_plot_setup2_display <- paste0( +# tables_doc_plot_setup2_display, +# # add landscape braces before R chunk +# "::: {.landscape}\n\n", +# add_chunk( +# paste0( +# "# plot split table ", i, "\n", +# tab_shortname, "_table_split_rda[[", i, "]] |>\n", +# " gt::tab_options(\n", +# " table.width = pct(100),\n", +# " table.layout = 'auto'\n", +# " ) |>\n", +# " gt::cols_width(\n", +# " everything() ~ px(144)\n", +# " ) \n" +# ), +# label = glue::glue("tbl-{tab_shortname}", i), +# add_option = TRUE, +# chunk_option = c( +# "echo: false", +# glue::glue( +# "tbl-cap: !expr paste0({tab_shortname}_cap, '(', {tab_shortname}_cap_split[[", i, "]], ')')" +# ) +# ) +# ), +# "\n", +# # add landscape braces after R chunk +# ":::\n" +# ) +# } +# +# tables_doc_plot_setup2 <- paste0( +# tables_doc_plot_setup2_import, +# tables_doc_plot_setup2_display +# ) +# } paste0( tables_doc_plot_setup1, From 70c188b2aeb4b14739ab1f6b2c6b7e3182f7e9a2 Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Mon, 23 Feb 2026 11:48:25 -0500 Subject: [PATCH 07/21] Redesign tables doc workflow to work with >1 table shown on landscape orientation --- R/create_tables_doc.R | 97 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 7 deletions(-) diff --git a/R/create_tables_doc.R b/R/create_tables_doc.R index 874ad42c..042928ce 100644 --- a/R/create_tables_doc.R +++ b/R/create_tables_doc.R @@ -177,7 +177,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n "\n" ) - ## add table if it only requires one chunk, on a portrait page + ## add table if it is intact on a portrait page if (tbl_class == "reg_reg") { tables_doc_plot_setup2 <- paste0( add_chunk( @@ -196,7 +196,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n ) } - ## add table if it only requires one chunk, rotated on a landscape page + ## add table if it is intact, rotated on a landscape page if (tbl_class == "wide_reg") { tables_doc_plot_setup2 <- paste0( # add landscape braces before R chunk @@ -228,8 +228,9 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n ) } - ## add table if it is split across portrait pages - if (tbl_class == "reg_long") { + ## add table if it is long enough to be shown on >1 portrait ("reg_long") OR landscape ("wide_long") page + ### only difference: latter has landscape braces + if (tbl_class == "reg_long" | tbl_class == "wide_long") { # identify number of split tables load(fs::path(tables_dir, "tables", tab)) split_tables <- length(table_list) @@ -260,6 +261,10 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n # add a chunk for each table tables_doc_plot_setup2_display <- paste0( tables_doc_plot_setup2_display, + # add landscape braces before R chunk if tbl_class == "wide_long" + ifelse(tbl_class == "wide_long", + "::: {.landscape}\n\n", + ""), add_chunk( paste0( "# plot split table ", i, "\n", @@ -282,7 +287,10 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n ) ) ), - "\n" + # add landscape braces after R chunk if tbl_class == "wide_long" + ifelse(tbl_class == "wide_long", + ":::\n", + "\n") ) } @@ -292,9 +300,84 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n ) } + ## add table if it is wide enough to be rotated and shown on >1 landscape ("ewide_reg") + if (tbl_class == "ewide_reg") { + if (split) { + # identify number of split tables + load(fs::path(tables_dir, "tables", tab)) + split_tables <- length(table_list) + } else { + # split extra-wide tables into smaller tables and export AND + # identify number of split tables IF not already split + split_tables <- export_split_tbls( + tables_dir = tables_dir, + plot_name = tab, + essential_columns = 1 + ) + } + + # add a chunk to import split tables + tables_doc_plot_setup2_import <- paste0( + add_chunk( + paste0( + "load(file.path(tables_dir, '", tab, "'))\n + # save rda with plot-specific name\n", + tab_shortname, "_table_split_rda <- table_list\n + # extract table caption specifiers\n", + tab_shortname, "_cap_split <- names(", tab_shortname, "_table_split_rda)" + ), + label = glue::glue("tbl-{tab_shortname}-labels"), + # add_option = TRUE, + chunk_option = c( + "echo: false", + "warnings: false", + glue::glue("include: false") + ) + ), + "\n" + ) + # prepare text for chunk that will display split tables + tables_doc_plot_setup2_display <- "" + for (i in 1:as.numeric(split_tables)) { + # add a chunk for each table + tables_doc_plot_setup2_display <- paste0( + tables_doc_plot_setup2_display, + # add landscape braces before R chunk + "::: {.landscape}\n\n", + add_chunk( + paste0( + "# plot split table ", i, "\n", + tab_shortname, "_table_split_rda[[", i, "]] |>\n", + " gt::tab_options(\n", + " table.width = pct(100),\n", + " table.layout = 'auto'\n", + " ) |>\n", + " gt::cols_width(\n", + " everything() ~ px(144)\n", + " ) \n" + ), + label = glue::glue("tbl-{tab_shortname}", i), + add_option = TRUE, + chunk_option = c( + "echo: false", + glue::glue( + "tbl-cap: !expr paste0({tab_shortname}_cap, '(', {tab_shortname}_cap_split[[", i, "]], ')')" + ) + ) + ), + "\n", + # add landscape braces after R chunk + ":::\n" + ) + } + + tables_doc_plot_setup2 <- paste0( + tables_doc_plot_setup2_import, + tables_doc_plot_setup2_display + ) + } - - # ## add table if it is rotated and split across landscape pages ("wide_long", "ewide_reg", "ewide_long") + ## add table if it is wide and long enough to be rotated and split across >1 landscape pages ("ewide_long") From 87759efa7b864e7c46900328183768bf345599cd Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Tue, 24 Feb 2026 15:27:05 -0500 Subject: [PATCH 08/21] Redesign tables doc workflow to work with remaining types of tables --- R/create_tables_doc.R | 187 +++++++++++++++++++----------------------- 1 file changed, 85 insertions(+), 102 deletions(-) diff --git a/R/create_tables_doc.R b/R/create_tables_doc.R index 042928ce..64c8bcdf 100644 --- a/R/create_tables_doc.R +++ b/R/create_tables_doc.R @@ -231,18 +231,78 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n ## add table if it is long enough to be shown on >1 portrait ("reg_long") OR landscape ("wide_long") page ### only difference: latter has landscape braces if (tbl_class == "reg_long" | tbl_class == "wide_long") { + # identify number of tables in rda + load(fs::path(tables_dir, "tables", tab)) + # split_tables <- length(table_list) + # identify number of tables that each split table must be further split + # into, with different rows per table + split_table_rows <- length(rda[[1]]$`_data`[[1]]) + split_tables_rowwise <- ceiling(split_table_rows/max_rows) + + # prepare text for chunk that will display split tables + tables_doc_plot_setup2 <- "" + for (i in 1:as.numeric(split_tables_rowwise)) { + # add a chunk for each table + tables_doc_plot_setup2 <- paste0( + tables_doc_plot_setup2, + # add landscape braces before R chunk if tbl_class == "wide_long" + ifelse(tbl_class == "wide_long", + "::: {.landscape}\n\n", + ""), + add_chunk( + paste0( + "# plot table ", i, "\n", + tab_shortname, "_table |>\n", + " gt::tab_options(\n", + " table.width = pct(100),\n", + " table.layout = 'auto'\n", + " ) |>\n", + " gt::cols_width(\n", + " everything() ~ px(144)) |>\n", + " gt::gt_split(row_every_n = ", max_rows, ") |>\n", + " gt::grp_pull(", i, ")\n" + ), + label = glue::glue("tbl-{tab_shortname}", i), + add_option = TRUE, + chunk_option = c( + "echo: false", + glue::glue( + "tbl-cap: !expr paste0({tab_shortname}_cap, '(', {tab_shortname}_cap, ')')" + ) + ) + ), + # add landscape braces after R chunk if tbl_class == "wide_long" + ifelse(tbl_class == "wide_long", + ":::\n", + "\n") + ) + } + } + + ## add table if it is wide enough to be rotated and shown on >1 landscape + if (tbl_class == "ewide_reg") { + if (split) { # identify number of split tables load(fs::path(tables_dir, "tables", tab)) split_tables <- length(table_list) + } else { + # split extra-wide tables into smaller tables and export AND + # identify number of split tables IF not already split + split_tables <- export_split_tbls( + tables_dir = tables_dir, + plot_name = tab, + essential_columns = 1 + ) + } # add a chunk to import split tables tables_doc_plot_setup2_import <- paste0( add_chunk( paste0( "load(file.path(tables_dir, '", tab, "'))\n - # save rda with plot-specific name\n", +# save rda with plot-specific name\n", tab_shortname, "_table_split_rda <- table_list\n - # extract table caption specifiers\n", +# extract table caption specifiers\n", tab_shortname, "_cap_split <- names(", tab_shortname, "_table_split_rda)" ), label = glue::glue("tbl-{tab_shortname}-labels"), @@ -261,10 +321,8 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n # add a chunk for each table tables_doc_plot_setup2_display <- paste0( tables_doc_plot_setup2_display, - # add landscape braces before R chunk if tbl_class == "wide_long" - ifelse(tbl_class == "wide_long", - "::: {.landscape}\n\n", - ""), + # add landscape braces before R chunk + "::: {.landscape}\n\n", add_chunk( paste0( "# plot split table ", i, "\n", @@ -274,9 +332,8 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n " table.layout = 'auto'\n", " ) |>\n", " gt::cols_width(\n", - " everything() ~ px(144) |>\n", - " gt::gt_split(row_every_n = ", max_length, ") |>\n", - " gt::grp_pull(", i, ")\n" + " everything() ~ px(144)\n", + " ) \n" ), label = glue::glue("tbl-{tab_shortname}", i), add_option = TRUE, @@ -287,10 +344,9 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n ) ) ), - # add landscape braces after R chunk if tbl_class == "wide_long" - ifelse(tbl_class == "wide_long", - ":::\n", - "\n") + "\n", + # add landscape braces after R chunk + ":::\n" ) } @@ -300,8 +356,8 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n ) } - ## add table if it is wide enough to be rotated and shown on >1 landscape ("ewide_reg") - if (tbl_class == "ewide_reg") { + ## add table if it is wide and long enough to be rotated and split across >1 landscape pages + if (tbl_class == "ewide_long"){ if (split) { # identify number of split tables load(fs::path(tables_dir, "tables", tab)) @@ -315,15 +371,19 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n essential_columns = 1 ) } - + # identify number of tables that each split table must be further split + # into, with different rows per table + split_table_rows <- length(table_list[[1]]$`_data`[[1]]) + split_tables_rowwise <- ceiling(split_table_rows/max_rows) + # add a chunk to import split tables tables_doc_plot_setup2_import <- paste0( add_chunk( paste0( "load(file.path(tables_dir, '", tab, "'))\n - # save rda with plot-specific name\n", +# save rda with plot-specific name\n", tab_shortname, "_table_split_rda <- table_list\n - # extract table caption specifiers\n", +# extract table caption specifiers\n", tab_shortname, "_cap_split <- names(", tab_shortname, "_table_split_rda)" ), label = glue::glue("tbl-{tab_shortname}-labels"), @@ -339,6 +399,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n # prepare text for chunk that will display split tables tables_doc_plot_setup2_display <- "" for (i in 1:as.numeric(split_tables)) { + for (j in 1:as.numeric(split_tables_rowwise)) { # add a chunk for each table tables_doc_plot_setup2_display <- paste0( tables_doc_plot_setup2_display, @@ -353,10 +414,11 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n " table.layout = 'auto'\n", " ) |>\n", " gt::cols_width(\n", - " everything() ~ px(144)\n", - " ) \n" + " everything() ~ px(144)) |>\n", + " gt::gt_split(row_every_n = ", max_rows, ") |>\n", + " gt::grp_pull(", j, ")\n" ), - label = glue::glue("tbl-{tab_shortname}", i), + label = glue::glue("tbl-{tab_shortname}", i, "-", j), add_option = TRUE, chunk_option = c( "echo: false", @@ -369,6 +431,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n # add landscape braces after R chunk ":::\n" ) + } } tables_doc_plot_setup2 <- paste0( @@ -376,87 +439,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n tables_doc_plot_setup2_display ) } - - ## add table if it is wide and long enough to be rotated and split across >1 landscape pages ("ewide_long") - - - -# if (tbl_orient == "extra-wide") { -# if (split) { -# # identify number of split tables -# load(fs::path(tables_dir, "tables", tab)) -# split_tables <- length(table_list) -# } else { -# # split extra-wide tables into smaller tables and export AND -# # identify number of split tables IF not already split -# split_tables <- export_split_tbls( -# tables_dir = tables_dir, -# plot_name = tab, -# essential_columns = 1 -# ) -# } -# -# # add a chunk to import split tables -# tables_doc_plot_setup2_import <- paste0( -# add_chunk( -# paste0( -# "load(file.path(tables_dir, '", tab, "'))\n -# # save rda with plot-specific name\n", -# tab_shortname, "_table_split_rda <- table_list\n -# # extract table caption specifiers\n", -# tab_shortname, "_cap_split <- names(", tab_shortname, "_table_split_rda)" -# ), -# label = glue::glue("tbl-{tab_shortname}-labels"), -# # add_option = TRUE, -# chunk_option = c( -# "echo: false", -# "warnings: false", -# glue::glue("include: false") -# ) -# ), -# "\n" -# ) -# # prepare text for chunk that will display split tables -# tables_doc_plot_setup2_display <- "" -# for (i in 1:as.numeric(split_tables)) { -# # add a chunk for each table -# tables_doc_plot_setup2_display <- paste0( -# tables_doc_plot_setup2_display, -# # add landscape braces before R chunk -# "::: {.landscape}\n\n", -# add_chunk( -# paste0( -# "# plot split table ", i, "\n", -# tab_shortname, "_table_split_rda[[", i, "]] |>\n", -# " gt::tab_options(\n", -# " table.width = pct(100),\n", -# " table.layout = 'auto'\n", -# " ) |>\n", -# " gt::cols_width(\n", -# " everything() ~ px(144)\n", -# " ) \n" -# ), -# label = glue::glue("tbl-{tab_shortname}", i), -# add_option = TRUE, -# chunk_option = c( -# "echo: false", -# glue::glue( -# "tbl-cap: !expr paste0({tab_shortname}_cap, '(', {tab_shortname}_cap_split[[", i, "]], ')')" -# ) -# ) -# ), -# "\n", -# # add landscape braces after R chunk -# ":::\n" -# ) -# } -# -# tables_doc_plot_setup2 <- paste0( -# tables_doc_plot_setup2_import, -# tables_doc_plot_setup2_display -# ) -# } - + paste0( tables_doc_plot_setup1, tables_doc_plot_setup2 From 217d5280b263d2abb0f68d0585edfbc37324f032 Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Tue, 24 Feb 2026 15:32:22 -0500 Subject: [PATCH 09/21] update tests by removing flextable and replacing with gt --- tests/testthat/test-ID_tbl_width_class.R | 8 ++++---- tests/testthat/test-render_lg_table.R | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/testthat/test-ID_tbl_width_class.R b/tests/testthat/test-ID_tbl_width_class.R index 8e15f8c1..86d3e19e 100644 --- a/tests/testthat/test-ID_tbl_width_class.R +++ b/tests/testthat/test-ID_tbl_width_class.R @@ -1,13 +1,13 @@ test_that("Table widths calculated correctly for wide table", { # make sample dataset - library(flextable) + library(gt) test_table <- data.frame( year = rep(seq(1987, 2024, 1), times = 3), estimate = rep(rnorm(38, mean = 1000, sd = 100), times = 6), label = rep(c("spawning_biomass", "catch", "abundance", "new_col1", "new_col2", "new_col3"), each = 38) ) |> tidyr::pivot_wider(id_cols = year, names_from = label, values_from = estimate) |> - flextable() + gt() rda <- list( "table" = test_table, @@ -34,7 +34,7 @@ test_that("Table widths calculated correctly for wide table", { test_that("Table widths calculated correctly for extra-wide table", { # make sample dataset - library(flextable) + library(gt) test_table <- data.frame( species = sample(c("Tuna", "Cod", "Trout", "Salmon"), 20, replace = TRUE), location = c( @@ -62,7 +62,7 @@ test_that("Table widths calculated correctly for extra-wide table", { is_tagged = sample(c(TRUE, FALSE), 20, replace = TRUE) ) |> tidyr::pivot_wider(id_cols = species, names_from = location, values_from = is_tagged) |> - flextable() + gt() rda <- list( "table" = test_table, diff --git a/tests/testthat/test-render_lg_table.R b/tests/testthat/test-render_lg_table.R index d4b799e2..cf19694e 100644 --- a/tests/testthat/test-render_lg_table.R +++ b/tests/testthat/test-render_lg_table.R @@ -4,7 +4,7 @@ test_that("accurate number of split tables identified", { t() |> as.data.frame() |> dplyr::select(1:50) |> - flextable::flextable() + gt::gt() dir.create("tables") @@ -32,7 +32,7 @@ test_that("accurate number of split tables identified", { t() |> as.data.frame() |> dplyr::select(1:70) |> - flextable::flextable() + gt::gt() # 0 essential columns tables_test3 <- render_lg_table(ft, @@ -58,7 +58,7 @@ test_that("accurate number of split tables identified", { t() |> as.data.frame() |> dplyr::select(1:20) |> - flextable::flextable() + gt::gt() # 0 essential columns tables_test5 <- render_lg_table(ft, @@ -88,7 +88,7 @@ test_that("table_list saved as an rda", { t() |> as.data.frame() |> dplyr::select(1:50) |> - flextable::flextable() + gt::gt() dir.create("tables") From 8a42396a537f78a082d7dbc0798025f04785407c Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Tue, 24 Feb 2026 15:47:39 -0500 Subject: [PATCH 10/21] sub flextable with gt in DESCRIPTION and devcontainer --- .devcontainer/devcontainer.json | 2 +- DESCRIPTION | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1ccdb03c..f519e60e 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -13,7 +13,7 @@ "packages": "qpdf" }, "ghcr.io/rocker-org/devcontainer-features/r-packages:1": { - "packages": "usethis,pak,qpdf,cli,flextable,forstringr,fs,naniar,officer,pdftools,prodlim,svDialogs,tidyselect,tinytex", + "packages": "usethis,pak,qpdf,cli,gt,forstringr,fs,naniar,officer,pdftools,prodlim,svDialogs,tidyselect,tinytex", "installSystemRequirements": true }, // option to run rstudio. you can type rserver into the command line to diff --git a/DESCRIPTION b/DESCRIPTION index c4f4f8d5..5ef3a5a7 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -25,10 +25,10 @@ Imports: cli, data.table, dplyr, - flextable, forstringr, fs, glue, + gt, naniar, officer, purrr, @@ -42,7 +42,6 @@ Imports: utils Suggests: ggplot2, - gt, kableExtra, knitr, parallel, From 0f50a272fbe9e5028c4803294e9770cd2f03e0f8 Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Tue, 24 Feb 2026 15:47:53 -0500 Subject: [PATCH 11/21] create tests for ID_tbl_length_class() --- tests/testthat/test-ID_tbl_length_class.R | 103 ++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 tests/testthat/test-ID_tbl_length_class.R diff --git a/tests/testthat/test-ID_tbl_length_class.R b/tests/testthat/test-ID_tbl_length_class.R new file mode 100644 index 00000000..362a4ff8 --- /dev/null +++ b/tests/testthat/test-ID_tbl_length_class.R @@ -0,0 +1,103 @@ +test_that("Table length calculated correctly for regular table", { + # make sample dataset + library(gt) + test_table <- data.frame( + year = rep(seq(1987, 2000, 1), times = 3), + estimate = rep(rnorm(42, mean = 1000, sd = 100)), + label = rep(c("spawning_biomass", "catch", "abundance"), each = 14) + ) |> + tidyr::pivot_wider(id_cols = year, names_from = label, values_from = estimate) |> + gt() + + rda <- list( + "table" = test_table, + "caption" = "test_caption" + ) + + tbl_path <- fs::path(getwd(), "tables") + dir.create(tbl_path) + save(rda, file = fs::path(getwd(), "tables", "indices.abundance_table.rda")) + + # regular length + tbl_width <- ID_tbl_length_class( + plot_name = "indices.abundance", + tables_dir = getwd() + ) + + expected_output <- "regular" + expect_equal(tbl_width, expected_output) + + # erase temporary testing files + unlink(tbl_path, recursive = T) +}) + +test_that("Table lengths calculated correctly for a long table", { + # make sample dataset + library(gt) + test_table <- data.frame( + species = sample(c("Tuna", "Cod", "Trout", "Salmon"), 20, replace = TRUE), + location = c( + "Providence_Rhode_Island", + "Narragansett_Bay", + "Point_Judith_Pond", + "Block_Island_Sound", + "Cape_Cod_Bay", + "Georges_Bank", + "Gulf_of_Maine", + "Montauk_New_York", + "Hudson_Canyon", + "Long_Island_Sound", + "Chesapeake_Bay", + "Outer_Banks_NC", + "Florida_Keys", + "Gulf_of_Mexico", + "Monterey_Bay_CA", + "Puget_Sound_WA", + "Kodiak_Alaska", + "Bering_Sea", + "Hawaiian_Islands", + "Sargasso_Sea", + "Buzzards_Bay_MA", + "Stellwagen_Bank", + "Vineyard_Sound", + "Great_South_Channel", + "Casco_Bay_Maine", + "Grand_Banks", + "Scotian_Shelf", + "Delaware_Bay", + "Pamlico_Sound_NC", + "Charleston_Bump_SC", + "Gray_Reef_GA", + "St_Johns_River_FL", + "Mobile_Bay_AL", + "Flower_Garden_Banks", + "Channel_Islands_CA", + "Columbia_River_Estuary", + "Gulf_of_the_Farallones", + "Aleutian_Islands", + "Chukchi_Sea", + "Beaufort_Sea" + ) + ) |> + gt() + + rda <- list( + "table" = test_table, + "caption" = "test_caption" + ) + + tbl_path <- fs::path(getwd(), "tables") + dir.create(tbl_path) + save(rda, file = fs::path(getwd(), "tables", "bnc_table.rda")) + + tbl_width2 <- ID_tbl_length_class( + plot_name = "bnc", + tables_dir = getwd() + ) + + expected_output <- "long" + expect_equal(tbl_width2, expected_output) + + # erase temporary testing files + unlink(tbl_path, recursive = T) +}) From ba1326a3d95f5fbf45386461e4d1a3bf43b2b7ef Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Tue, 24 Feb 2026 16:27:45 -0500 Subject: [PATCH 12/21] Update tests --- tests/testthat/test-create_tables_doc.R | 228 ++++++++++++------------ tests/testthat/test-export_split_tbls.R | 120 +++++++++---- 2 files changed, 200 insertions(+), 148 deletions(-) diff --git a/tests/testthat/test-create_tables_doc.R b/tests/testthat/test-create_tables_doc.R index 9bdbbcde..ec3d1c56 100644 --- a/tests/testthat/test-create_tables_doc.R +++ b/tests/testthat/test-create_tables_doc.R @@ -27,117 +27,117 @@ test_that("Creates expected start of nearly empty tables doc", { file.remove(fs::path(getwd(), "08_tables.qmd")) }) -# test_that("Creates expected start of tables doc with table", { -# # load sample dataset -# load(file.path( -# "fixtures", "ss3_models_converted", "Hake_2018", -# "std_output.rda" -# )) -# -# stockplotr::table_landings(out_new, -# make_rda = TRUE -# ) -# -# # create tables doc -# create_tables_doc( -# subdir = getwd(), -# tables_dir = getwd() -# ) -# -# # read in tables doc -# table_content <- readLines("08_tables.qmd") -# # extract first 7 lines -# head_table_content <- head(table_content, 7) -# # remove line numbers and collapse -# fc_pasted <- paste(head_table_content, collapse = "") -# -# # expected tables doc head -# expected_head_table_content <- "# Tables {#sec-tables} ```{r} #| label: 'set-rda-dir-tbls'#| echo: false #| warning: false #| include: false" -# -# # test expectation of start of tables doc -# testthat::expect_equal( -# fc_pasted, -# expected_head_table_content -# ) -# -# # erase temporary testing files -# file.remove(fs::path(getwd(), "08_tables.qmd")) -# file.remove(fs::path(getwd(), "captions_alt_text.csv")) -# unlink(fs::path(getwd(), "tables"), recursive = T) -# }) - -# test_that("Throws warning if chunks with identical labels", { -# # load sample dataset -# load(file.path( -# "fixtures", "ss3_models_converted", "Hake_2018", -# "std_output.rda" -# )) -# -# stockplotr::table_landings(out_new, -# make_rda = TRUE -# ) -# -# # create tables doc -# create_tables_doc( -# subdir = getwd(), -# tables_dir = getwd() -# ) -# -# expect_message( -# create_tables_doc( -# subdir = getwd(), -# tables_dir = getwd() -# ), -# "Tables doc will not render if chunks have identical labels." -# ) -# -# # erase temporary testing files -# file.remove(fs::path(getwd(), "08_tables.qmd")) -# file.remove(fs::path(getwd(), "captions_alt_text.csv")) -# unlink(fs::path(getwd(), "tables"), recursive = T) -# }) - -# test_that("Formerly empty tables doc renders correctly", { -# # create empty tables doc -# create_template() -# -# # load sample dataset -# load(file.path( -# "fixtures", "ss3_models_converted", "Hake_2018", -# "std_output.rda" -# )) -# -# stockplotr::table_landings( -# dat = out_new, -# make_rda = TRUE, -# module = "TIME_SERIES" -# ) -# -# # rerender tables doc, appending new table -# create_tables_doc( -# subdir = file.path(getwd(), "report"), -# tables_dir = getwd() -# ) -# -# # read in tables doc -# table_content <- readLines(file.path(getwd(), "report", "08_tables.qmd")) -# # extract first 8 lines -# head_table_content <- head(table_content, 8) -# # remove line numbers and collapse -# fc_pasted <- paste(head_table_content, collapse = "") -# -# # expected tables doc head -# expected_head_table_content <- "# Tables {#sec-tables} ```{r} #| label: 'set-rda-dir-figs'#| echo: false #| warnings: false #| eval: true" -# -# # test expectation of start of tables doc -# expect_equal( -# fc_pasted, -# expected_head_table_content -# ) -# -# # erase temporary testing files -# file.remove(fs::path(getwd(), "08_tables.qmd")) -# file.remove(fs::path(getwd(), "captions_alt_text.csv")) -# unlink(fs::path(getwd(), "tables"), recursive = T) -# unlink(fs::path(getwd(), "report"), recursive = T) -# }) +test_that("Creates expected start of tables doc with table", { + # load sample dataset + load(file.path( + "fixtures", "ss3_models_converted", "Hake_2018", + "std_output.rda" + )) + + stockplotr::table_landings(out_new, + make_rda = TRUE + ) + + # create tables doc + create_tables_doc( + subdir = getwd(), + tables_dir = getwd() + ) + + # read in tables doc + table_content <- readLines("08_tables.qmd") + # extract first 7 lines + head_table_content <- head(table_content, 7) + # remove line numbers and collapse + fc_pasted <- paste(head_table_content, collapse = "") + + # expected tables doc head + expected_head_table_content <- "# Tables {#sec-tables} ```{r} #| label: 'set-rda-dir-tbls'#| echo: false #| warning: false #| include: false" + + # test expectation of start of tables doc + testthat::expect_equal( + fc_pasted, + expected_head_table_content + ) + + # erase temporary testing files + file.remove(fs::path(getwd(), "08_tables.qmd")) + file.remove(fs::path(getwd(), "captions_alt_text.csv")) + unlink(fs::path(getwd(), "tables"), recursive = T) +}) + +test_that("Throws warning if chunks with identical labels", { + # load sample dataset + load(file.path( + "fixtures", "ss3_models_converted", "Hake_2018", + "std_output.rda" + )) + + stockplotr::table_landings(out_new, + make_rda = TRUE + ) + + # create tables doc + create_tables_doc( + subdir = getwd(), + tables_dir = getwd() + ) + + expect_message( + create_tables_doc( + subdir = getwd(), + tables_dir = getwd() + ), + "Tables doc will not render if chunks have identical labels." + ) + + # erase temporary testing files + file.remove(fs::path(getwd(), "08_tables.qmd")) + file.remove(fs::path(getwd(), "captions_alt_text.csv")) + unlink(fs::path(getwd(), "tables"), recursive = T) +}) + +test_that("Formerly empty tables doc renders correctly", { + # create empty tables doc + create_template() + + # load sample dataset + load(file.path( + "fixtures", "ss3_models_converted", "Hake_2018", + "std_output.rda" + )) + + stockplotr::table_landings( + dat = out_new, + make_rda = TRUE, + module = "TIME_SERIES" + ) + + # rerender tables doc, appending new table + create_tables_doc( + subdir = file.path(getwd(), "report"), + tables_dir = getwd() + ) + + # read in tables doc + table_content <- readLines(file.path(getwd(), "report", "08_tables.qmd")) + # extract first 8 lines + head_table_content <- head(table_content, 8) + # remove line numbers and collapse + fc_pasted <- paste(head_table_content, collapse = "") + + # expected tables doc head + expected_head_table_content <- "# Tables {#sec-tables} ```{r} #| label: 'set-rda-dir-figs'#| echo: false #| warnings: false #| eval: true" + + # test expectation of start of tables doc + expect_equal( + fc_pasted, + expected_head_table_content + ) + + # erase temporary testing files + file.remove(fs::path(getwd(), "08_tables.qmd")) + file.remove(fs::path(getwd(), "captions_alt_text.csv")) + unlink(fs::path(getwd(), "tables"), recursive = T) + unlink(fs::path(getwd(), "report"), recursive = T) +}) diff --git a/tests/testthat/test-export_split_tbls.R b/tests/testthat/test-export_split_tbls.R index 47987b62..362f5af6 100644 --- a/tests/testthat/test-export_split_tbls.R +++ b/tests/testthat/test-export_split_tbls.R @@ -1,34 +1,86 @@ -# test_that("Number of split tables is calculated correctly for converted bam model", { -# # read in sample dataset -# dat <- utils::read.csv( -# file = fs::path("fixtures", "bam_models_converted", "bsb_conout.csv") -# ) -# -# # bam model with table split into 2 -# stockplotr::table_indices(dat, -# make_rda = TRUE -# ) -# -# # indices table -# num_tabs <- export_split_tbls( -# tables_dir = getwd(), -# plot_name = "indices.abundance_table.rda", -# essential_columns = 1 -# ) -# -# # expect 2 tables -# expected_output <- 2 -# expect_equal(num_tabs, expected_output) -# -# # expect to see an "indices.abundance_table_split.rda" file -# expect_no_error("indices.abundance_table_split.rda" %in% list.files(file.path("tables"))) -# -# # expect that an object "table_list" imported into environment -# expect_no_error(load(file.path("tables", "indices.abundance_table_split.rda"))) -# -# expect_equal(length(table_list), 2) -# -# # erase temporary testing files -# file.remove(fs::path(getwd(), "captions_alt_text.csv")) -# unlink(fs::path(getwd(), "tables"), recursive = T) -# }) +test_that("Number of split tables is calculated correctly for 1 table", { + library(gt) + load(file.path( + "fixtures", "ss3_models_converted", "Hake_2018", + "std_output.rda" + )) + + stockplotr::table_landings(out_new, + module = "CATCH", + make_rda = TRUE + ) + + + # indices table + num_tabs <- export_split_tbls( + tables_dir = getwd(), + plot_name = "landings_table.rda", + essential_columns = 1 + ) + + # expect 1 table + expected_output <- 1 + expect_equal(num_tabs, expected_output) + + # erase temporary testing files + file.remove(fs::path(getwd(), "captions_alt_text.csv")) + unlink(fs::path(getwd(), "tables"), recursive = T) +}) + + +test_that("Number of split tables is calculated correctly for 2 tables", { + library(gt) + load(file.path( + "fixtures", "ss3_models_converted", "Hake_2018", + "std_output.rda" + )) + + stockplotr::table_landings(out_new, + module = "CATCH", + make_rda = TRUE + ) + + load(file.path("tables", "landings_table.rda")) + + wider_df <- cbind( + rda$table$`_data`, + data.frame( + Col1 = 1:52, + Col2 = 52:103, + Col3 = 104:155, + Col4 = 156:207, + Col5 = 208:259, + Col6 = 260:311, + Col7 = 312:363, + Col8 = 364:415 + ) + ) |> + gt() + + rda$table <- wider_df + save(rda, file = file.path(getwd(), "tables", "landings_table.rda")) + + # indices table + num_tabs <- export_split_tbls( + tables_dir = getwd(), + plot_name = "landings_table.rda", + essential_columns = 1 + ) + + # expect 2 tables + expected_output <- 2 + expect_equal(num_tabs, expected_output) + + # expect to see an "landings_table_split.rda" file + expect_no_error("landings_table_split.rda" %in% list.files(file.path("tables"))) + + # expect that an object "table_list" imported into environment + expect_no_error(load(file.path("tables", "landings_table_split.rda"))) + + expect_equal(length(table_list), 2) + + # erase temporary testing files + file.remove(fs::path(getwd(), "captions_alt_text.csv")) + unlink(fs::path(getwd(), "tables"), recursive = T) + +}) From 4f8de4db6a7739da5b6fae3a166988a5281b8f02 Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Tue, 24 Feb 2026 16:57:57 -0500 Subject: [PATCH 13/21] Update captions for split tables by removing individual column names shown, and instead state number of table vs total number of tables --- R/create_tables_doc.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/create_tables_doc.R b/R/create_tables_doc.R index 64c8bcdf..dc6a51e6 100644 --- a/R/create_tables_doc.R +++ b/R/create_tables_doc.R @@ -267,7 +267,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n chunk_option = c( "echo: false", glue::glue( - "tbl-cap: !expr paste0({tab_shortname}_cap, '(', {tab_shortname}_cap, ')')" + "tbl-cap: !expr paste0({tab_shortname}_cap, ' ({i} of {split_tables_rowwise})')" ) ) ), @@ -340,7 +340,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n chunk_option = c( "echo: false", glue::glue( - "tbl-cap: !expr paste0({tab_shortname}_cap, '(', {tab_shortname}_cap_split[[", i, "]], ')')" + "tbl-cap: !expr paste0({tab_shortname}_cap, ' (', {tab_shortname}_cap_split[[", i, "]], ') ({i} of {split_tables_rowwise})')" ) ) ), From 342437e0ff3b28c3f7bacbb720c42edc044c871c Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Wed, 25 Feb 2026 11:44:39 -0500 Subject: [PATCH 14/21] Update render_lg_table() to work with varying essential column values, and redesign to better work with gt tables vs flextables; update documentation and tests --- R/render_lg_table.R | 187 +++++++++--------------- man/export_split_tbls.Rd | 2 +- man/render_lg_table.Rd | 2 +- tests/testthat/test-create_tables_doc.R | 4 +- tests/testthat/test-export_split_tbls.R | 8 +- tests/testthat/test-render_lg_table.R | 19 ++- 6 files changed, 89 insertions(+), 133 deletions(-) diff --git a/R/render_lg_table.R b/R/render_lg_table.R index a56c0d7c..30803f58 100644 --- a/R/render_lg_table.R +++ b/R/render_lg_table.R @@ -3,7 +3,7 @@ #' @param report_gt The extra-wide gt table. #' @param essential_columns The columns that will be retained between the split #' tables, formatted as a sequence (e.g., 1:2 for columns 1-2, or 1 for a single -#' column. Example: for the indices table, this could be the year column. +#' column). Example: for the indices table, this could be the year column. #' @param plot_name Name of the .rda file containing the table #' @inheritParams create_tables_doc #' @@ -31,141 +31,94 @@ render_lg_table <- function(report_gt, essential_columns, tables_dir, plot_name) { - - # Set each col to 1.5" + + # Clean up essential_columns + is_empty_essentials <- is.null(essential_columns) || + length(essential_columns) == 0 || + (length(essential_columns) == 1 && essential_columns == "0") + + if (is_empty_essentials) { + essential_cols_cleaned <- character(0) + } else { + # If numeric (like 1:2), convert to actual column names to prevent index shifting errors + if (is.numeric(essential_columns)) { + essential_cols_cleaned <- colnames(report_gt[["_data"]])[essential_columns] + } else { + essential_cols_cleaned <- essential_columns + } + } + col_inches <- 1.5 # calculate key numbers # total columns, width of table in inches total_cols <- ncol(report_gt[["_data"]]) total_width <- total_cols * col_inches - + # goal width of each table split from report_gt, in inches goal_width <- 7.5 # max is 8" - + # goal number of columns per table split from report_gt goal_cols_per_table <- ceiling(goal_width / col_inches) - - # split tables needed - num_tables <- ceiling(total_cols / goal_cols_per_table) - - # set up main df organizing split table information - table_cols <- matrix(NA, - nrow = num_tables, - ncol = 4 - ) |> - as.data.frame() |> - dplyr::rename( - "table" = 1, - "cols_to_keep" = 2, - "start_col" = 3, - "end_col" = 4 - ) - - i <- 1 - # TODO: add error improved error message - essential_cols <- essential_columns - for (i in 1:num_tables) { - # set table number to i - table_num <- i - - # add table number to df - table_cols$table[i] <- i - - # get first and last columns of new table - if (i == 1) { - init_col <- 1 - end_col <- init_col + goal_cols_per_table - } else if ((i < num_tables) & (init_col < total_cols)) { - # subtracting essential_cols so the first cols will be the essential ones - # and the total cols will still = goal_cols_per_table - end_col <- init_col + goal_cols_per_table - length(essential_cols) - } else { - end_col <- total_cols - } - - table_cols$cols_to_keep[i] <- paste0(init_col, ":", end_col) - - table_cols$start_col[i] <- init_col - table_cols$end_col[i] <- end_col - - # add goal_cols_per_table for next table - init_col <- end_col + 1 - - # add 1 for next table - i <- i + 1 - } - - # add col with essential cols - table_cols <- table_cols |> - dplyr::mutate( - essential_cols = paste(essential_cols, collapse = ", "), - - # find cols to delete - cols_to_del = apply(table_cols, 1, function(row) { - curr_range <- row["start_col"]:row["end_col"] - miss_nums <- setdiff(1:total_cols, curr_range) - paste(miss_nums, collapse = ", ") - }), - # make cols_to_delete and essential_cols into sequences - cols_to_del_seq = lapply(cols_to_del, function(x) as.numeric(unlist(strsplit(x, ",")))), - essential_cols_seq = lapply(essential_cols, function(x) as.numeric(unlist(strsplit(x, ",")))), - # find the final columns to delete by removing essential_cols from - # cols_to_delete - final_cols_to_del = mapply(function(seq1, seq2) { - paste(setdiff(seq1, seq2), collapse = ", ") - }, cols_to_del_seq, essential_cols_seq) - ) - - - # save all tables to a list + + all_cols <- colnames(report_gt[["_data"]]) + moving_cols <- setdiff(all_cols, essential_cols_cleaned) + + total_slots <- floor(goal_width / col_inches) + + # Slots for non-essential columns + non_ess_slots <- max(1, total_slots - length(essential_cols_cleaned)) + + # Chunk the moving columns + col_chunks <- split(moving_cols, + ceiling(seq_along(moving_cols) / non_ess_slots)) + + # Loop through chunks and build tables table_list <- list() - for (i in 1:num_tables) { + for (i in seq_along(col_chunks)) { + + current_keep <- c(essential_cols_cleaned, col_chunks[[i]]) + + # Create the gt objects split_table <- report_gt |> - gt::cols_hide( - columns = all_of( - as.numeric( - unlist( - strsplit( - table_cols[i, "final_cols_to_del"], "," - ) - ) - ) - ) - ) |> - gt::tab_options(table.width = px(goal_width * 96)) |> # in pixels + gt::cols_hide(columns = -all_of(current_keep)) |> + gt::tab_options(table.width = px(goal_width * 96)) |> gt::tab_style( - style = cell_borders( - sides = "bottom", - color = "#666666", - weight = px(1.5) - ), - locations = cells_column_labels() + style = gt::cell_borders(sides = "bottom", + color = "#666666", + weight = px(1.5)), + locations = gt::cells_column_labels() ) |> gt::tab_style( - style = cell_text(v_align = "middle"), - locations = cells_column_labels() + style = gt::cell_text(v_align = "middle"), + locations = gt::cells_column_labels() ) - + + # Store table in list table_list[[i]] <- split_table - - # get rownames of split table + + # Get labels for visible columns only all_vals <- split_table[["_boxhead"]] - split_tbl_vals <- all_vals |> - dplyr::filter(type != "hidden") |> - # Pull the display labels (this handles cases where you renamed columns) - dplyr::pull(column_label) |> - as.character() |> - # Replaces subset() - filter keeps only the values that match - (\(x) x[!is.na(x) & x != ""])() |> - unique() |> - stringr::str_squish() |> - paste(collapse = ", ") - - # add rownames to table_list - names(table_list)[[i]] <- split_tbl_vals + if (!is.null(all_vals)) { + display_names <- all_vals |> + dplyr::filter(type != "hidden") |> + dplyr::pull(column_label) |> + as.character() |> + (\(x) x[!is.na(x) & x != ""])() |> + unique() |> + stringr::str_squish() |> + paste(collapse = ", ") + + # Assign name to list index + if (nchar(display_names) > 0) { + names(table_list)[i] <- display_names + } else { + names(table_list)[i] <- paste0("Table_Part_", i) + } + } } + # save table_list as rda save(table_list, file = fs::path( @@ -175,5 +128,5 @@ render_lg_table <- function(report_gt, ) ) - num_tables + length(table_list) } diff --git a/man/export_split_tbls.Rd b/man/export_split_tbls.Rd index dcd184f7..245bfa7f 100644 --- a/man/export_split_tbls.Rd +++ b/man/export_split_tbls.Rd @@ -18,7 +18,7 @@ files.} \item{essential_columns}{The columns that will be retained between the split tables, formatted as a sequence (e.g., 1:2 for columns 1-2, or 1 for a single -column. Example: for the indices table, this could be the year column.} +column). Example: for the indices table, this could be the year column.} } \value{ The number of split tables diff --git a/man/render_lg_table.Rd b/man/render_lg_table.Rd index 26b59a0e..d8f39174 100644 --- a/man/render_lg_table.Rd +++ b/man/render_lg_table.Rd @@ -11,7 +11,7 @@ render_lg_table(report_gt, essential_columns, tables_dir, plot_name) \item{essential_columns}{The columns that will be retained between the split tables, formatted as a sequence (e.g., 1:2 for columns 1-2, or 1 for a single -column. Example: for the indices table, this could be the year column.} +column). Example: for the indices table, this could be the year column.} \item{tables_dir}{The location of the "tables" folder, which contains tables files.} diff --git a/tests/testthat/test-create_tables_doc.R b/tests/testthat/test-create_tables_doc.R index ec3d1c56..bddebda9 100644 --- a/tests/testthat/test-create_tables_doc.R +++ b/tests/testthat/test-create_tables_doc.R @@ -110,7 +110,7 @@ test_that("Formerly empty tables doc renders correctly", { stockplotr::table_landings( dat = out_new, make_rda = TRUE, - module = "TIME_SERIES" + module = "CATCH" ) # rerender tables doc, appending new table @@ -127,7 +127,7 @@ test_that("Formerly empty tables doc renders correctly", { fc_pasted <- paste(head_table_content, collapse = "") # expected tables doc head - expected_head_table_content <- "# Tables {#sec-tables} ```{r} #| label: 'set-rda-dir-figs'#| echo: false #| warnings: false #| eval: true" + expected_head_table_content <- "# Tables {#sec-tables} ```{r} #| label: 'set-rda-dir-tbls'#| echo: false #| warning: false #| include: false" # test expectation of start of tables doc expect_equal( diff --git a/tests/testthat/test-export_split_tbls.R b/tests/testthat/test-export_split_tbls.R index 362f5af6..9e88b106 100644 --- a/tests/testthat/test-export_split_tbls.R +++ b/tests/testthat/test-export_split_tbls.R @@ -28,7 +28,7 @@ test_that("Number of split tables is calculated correctly for 1 table", { }) -test_that("Number of split tables is calculated correctly for 2 tables", { +test_that("Number of split tables is calculated correctly for 3 tables", { library(gt) load(file.path( "fixtures", "ss3_models_converted", "Hake_2018", @@ -67,8 +67,8 @@ test_that("Number of split tables is calculated correctly for 2 tables", { essential_columns = 1 ) - # expect 2 tables - expected_output <- 2 + # expect 3 tables + expected_output <- 3 expect_equal(num_tabs, expected_output) # expect to see an "landings_table_split.rda" file @@ -77,7 +77,7 @@ test_that("Number of split tables is calculated correctly for 2 tables", { # expect that an object "table_list" imported into environment expect_no_error(load(file.path("tables", "landings_table_split.rda"))) - expect_equal(length(table_list), 2) + expect_equal(length(table_list), 3) # erase temporary testing files file.remove(fs::path(getwd(), "captions_alt_text.csv")) diff --git a/tests/testthat/test-render_lg_table.R b/tests/testthat/test-render_lg_table.R index cf19694e..a7fad84e 100644 --- a/tests/testthat/test-render_lg_table.R +++ b/tests/testthat/test-render_lg_table.R @@ -1,4 +1,5 @@ test_that("accurate number of split tables identified", { + library(gt) # make very wide table ft <- as.data.frame(faithful) |> t() |> @@ -9,22 +10,24 @@ test_that("accurate number of split tables identified", { dir.create("tables") # 0 essential columns - tables_test1 <- render_lg_table(ft, + tables_test1 <- render_lg_table( + report_gt = ft, essential_columns = 0, tables_dir = getwd(), plot_name = "test_plot1.rda" ) - expect_equal(tables_test1, 5) + expect_equal(tables_test1, 10) # 2 essential columns - tables_test2 <- render_lg_table(ft, + tables_test2 <- render_lg_table( + report_gt = ft, essential_columns = 1:2, tables_dir = getwd(), plot_name = "test_plot2.rda" ) - expect_equal(tables_test2, 5) + expect_equal(tables_test2, 16) # make very very wide table @@ -41,7 +44,7 @@ test_that("accurate number of split tables identified", { plot_name = "test_plot3.rda" ) - expect_equal(tables_test3, 7) + expect_equal(tables_test3, 14) # 3 essential columns tables_test4 <- render_lg_table(ft, @@ -50,7 +53,7 @@ test_that("accurate number of split tables identified", { plot_name = "test_plot4.rda" ) - expect_equal(tables_test4, 7) + expect_equal(tables_test4, 34) # make slightly wide table @@ -67,7 +70,7 @@ test_that("accurate number of split tables identified", { plot_name = "test_plot5.rda" ) - expect_equal(tables_test5, 2) + expect_equal(tables_test5, 4) # 3 essential columns tables_test6 <- render_lg_table(ft, @@ -76,7 +79,7 @@ test_that("accurate number of split tables identified", { plot_name = "test_plot6.rda" ) - expect_equal(tables_test6, 2) + expect_equal(tables_test6, 9) # erase temporary testing files unlink(fs::path(getwd(), "tables"), recursive = T) From c7d63e51ec24c67d5c26e67cd2936e431d945136 Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Wed, 25 Feb 2026 11:55:10 -0500 Subject: [PATCH 15/21] Delete unusable, duplicated license --- LICENSE | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 LICENSE diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 8c0df26d..00000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 Schiano-NOAA - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. From 83f0b7bcecb7903a082f7be49d9ba1fa9004e54e Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Wed, 25 Feb 2026 12:05:20 -0500 Subject: [PATCH 16/21] Address warnings in R cmd check --- DESCRIPTION | 1 - R/asar-package.R | 2 +- R/render_lg_table.R | 6 +++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5ef3a5a7..1e3eaab9 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -30,7 +30,6 @@ Imports: glue, gt, naniar, - officer, purrr, stats, stringi, diff --git a/R/asar-package.R b/R/asar-package.R index ee23b68a..16039ed9 100644 --- a/R/asar-package.R +++ b/R/asar-package.R @@ -17,6 +17,6 @@ globvar <- c( "row_rescale", "table_list", "read.csv", "All", "x", "Acronym", "Meaning", "meaning_lower", "Definition", "n", "Label", "figures_doc_header", "X", "len_bins", "area", "match_key", "uncertainty_label", - "word_count", "ac_short" + "word_count", "ac_short", "column_label" ) if (getRversion() >= "2.15.1") utils::globalVariables(globvar) diff --git a/R/render_lg_table.R b/R/render_lg_table.R index 30803f58..08568251 100644 --- a/R/render_lg_table.R +++ b/R/render_lg_table.R @@ -81,12 +81,12 @@ render_lg_table <- function(report_gt, # Create the gt objects split_table <- report_gt |> - gt::cols_hide(columns = -all_of(current_keep)) |> - gt::tab_options(table.width = px(goal_width * 96)) |> + gt::cols_hide(columns = -dplyr::all_of(current_keep)) |> + gt::tab_options(table.width = gt::px(goal_width * 96)) |> gt::tab_style( style = gt::cell_borders(sides = "bottom", color = "#666666", - weight = px(1.5)), + weight = gt::px(1.5)), locations = gt::cells_column_labels() ) |> gt::tab_style( From 91207aee8f0fa3241627469b5ef179617c5b4b09 Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Wed, 25 Feb 2026 15:35:13 -0500 Subject: [PATCH 17/21] Comment out code that restricts table column width unnecessarily, since tab_options(table.width) is sufficient; add "tbl-pos: 't'" to table chunk options to anchor tables --- R/create_tables_doc.R | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/R/create_tables_doc.R b/R/create_tables_doc.R index dc6a51e6..be7b9db7 100644 --- a/R/create_tables_doc.R +++ b/R/create_tables_doc.R @@ -189,7 +189,8 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n "warnings: false", glue::glue( "tbl-cap: !expr {tab_shortname}_cap" - ) + ), + "tbl-pos: 't'" ) ), "\n" @@ -207,10 +208,10 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n " gt::tab_options(\n", " table.width = pct(100),\n", " table.layout = 'auto'\n", - " ) |>\n", - " gt::cols_width(\n", - " everything() ~ px(144)\n", - " )" + " ) |>\n"#, + # " gt::cols_width(\n", + # " everything() ~ px(144)\n", + # " )" ), label = glue::glue("tbl-{tab_shortname}"), # add_option = TRUE, @@ -219,7 +220,8 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n "warnings: false", glue::glue( "tbl-cap: !expr {tab_shortname}_cap" - ) + ), + "tbl-pos: 't'" ) ), "\n", @@ -257,8 +259,8 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n " table.width = pct(100),\n", " table.layout = 'auto'\n", " ) |>\n", - " gt::cols_width(\n", - " everything() ~ px(144)) |>\n", + # " gt::cols_width(\n", + # " everything() ~ px(144)) |>\n", " gt::gt_split(row_every_n = ", max_rows, ") |>\n", " gt::grp_pull(", i, ")\n" ), @@ -268,7 +270,8 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n "echo: false", glue::glue( "tbl-cap: !expr paste0({tab_shortname}_cap, ' ({i} of {split_tables_rowwise})')" - ) + ), + "tbl-pos: 't'" ) ), # add landscape braces after R chunk if tbl_class == "wide_long" @@ -330,10 +333,10 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n " gt::tab_options(\n", " table.width = pct(100),\n", " table.layout = 'auto'\n", - " ) |>\n", - " gt::cols_width(\n", - " everything() ~ px(144)\n", - " ) \n" + " ) |>\n"#, + # " gt::cols_width(\n", + # " everything() ~ px(144)\n", + # " ) \n" ), label = glue::glue("tbl-{tab_shortname}", i), add_option = TRUE, @@ -341,7 +344,8 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n "echo: false", glue::glue( "tbl-cap: !expr paste0({tab_shortname}_cap, ' (', {tab_shortname}_cap_split[[", i, "]], ') ({i} of {split_tables_rowwise})')" - ) + ), + "tbl-pos: 't'" ) ), "\n", @@ -413,8 +417,8 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n " table.width = pct(100),\n", " table.layout = 'auto'\n", " ) |>\n", - " gt::cols_width(\n", - " everything() ~ px(144)) |>\n", + # " gt::cols_width(\n", + # " everything() ~ px(144)) |>\n", " gt::gt_split(row_every_n = ", max_rows, ") |>\n", " gt::grp_pull(", j, ")\n" ), @@ -424,7 +428,8 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n "echo: false", glue::glue( "tbl-cap: !expr paste0({tab_shortname}_cap, '(', {tab_shortname}_cap_split[[", i, "]], ')')" - ) + ), + "tbl-pos: 't'" ) ), "\n", From 4283149145c75ca80ce0ad05369449ea28dd83c2 Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Wed, 25 Feb 2026 16:37:21 -0500 Subject: [PATCH 18/21] Edit add_child() to add line to tables chunk in skeleton to add "clearpage" latex command --- R/add_child.R | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/R/add_child.R b/R/add_child.R index 31ba9fa5..7e03bcce 100644 --- a/R/add_child.R +++ b/R/add_child.R @@ -28,6 +28,10 @@ add_child <- function(x, "#| warning: false", "\n", "a <- knitr::knit_child(", "'", sec_num, "'", ", quiet = TRUE)", "\n", "cat(a, sep = '\\n')", "\n", + # ifelse checks for the 'tables' label and injects the extra code/comment + ifelse(label[i] == "tables", + "# Force LaTeX to render all floating tables before moving to the next section\ncat('\\n\\\\clearpage\\n')\n", + ""), "```", "\n" ) child <- paste(child, child_loop, sep = "\n {{< pagebreak >}} \n") From 13400503485ce7e0a79013f94f3ef55d9cb6bd2a Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Wed, 25 Feb 2026 17:00:34 -0500 Subject: [PATCH 19/21] Update captions for tables split by columns to remove mention of specific columns in the table --- R/create_tables_doc.R | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/R/create_tables_doc.R b/R/create_tables_doc.R index be7b9db7..24a406a2 100644 --- a/R/create_tables_doc.R +++ b/R/create_tables_doc.R @@ -208,7 +208,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n " gt::tab_options(\n", " table.width = pct(100),\n", " table.layout = 'auto'\n", - " ) |>\n"#, + " ) \n"#|>, # " gt::cols_width(\n", # " everything() ~ px(144)\n", # " )" @@ -333,7 +333,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n " gt::tab_options(\n", " table.width = pct(100),\n", " table.layout = 'auto'\n", - " ) |>\n"#, + " ) \n"#|>, # " gt::cols_width(\n", # " everything() ~ px(144)\n", # " ) \n" @@ -343,7 +343,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n chunk_option = c( "echo: false", glue::glue( - "tbl-cap: !expr paste0({tab_shortname}_cap, ' (', {tab_shortname}_cap_split[[", i, "]], ') ({i} of {split_tables_rowwise})')" + "tbl-cap: !expr paste0({tab_shortname}_cap, ' ({i} of {split_tables})')" ), "tbl-pos: 't'" ) @@ -427,7 +427,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n chunk_option = c( "echo: false", glue::glue( - "tbl-cap: !expr paste0({tab_shortname}_cap, '(', {tab_shortname}_cap_split[[", i, "]], ')')" + "tbl-cap: !expr paste0({tab_shortname}_cap, ' ({i} of {split_tables} tables split by column, {j} of {split_tables_rowwise} tables split by rows)')" ), "tbl-pos: 't'" ) From 38a4c85e28b4334581b046d9bf470aecb9513865 Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Thu, 26 Feb 2026 16:50:19 -0500 Subject: [PATCH 20/21] Set col widths for tables; fix issue with extra-wide tables --- R/create_tables_doc.R | 44 +++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/R/create_tables_doc.R b/R/create_tables_doc.R index 24a406a2..8c51e84e 100644 --- a/R/create_tables_doc.R +++ b/R/create_tables_doc.R @@ -181,7 +181,11 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n if (tbl_class == "reg_reg") { tables_doc_plot_setup2 <- paste0( add_chunk( - glue::glue("{tab_shortname}_table"), + glue::glue("{tab_shortname}_table |>\n", + " gt::cols_width(\n", + " everything() ~ pct(20)\n", + " ) \n" + ), label = glue::glue("tbl-{tab_shortname}"), # add_option = TRUE, chunk_option = c( @@ -208,10 +212,10 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n " gt::tab_options(\n", " table.width = pct(100),\n", " table.layout = 'auto'\n", - " ) \n"#|>, - # " gt::cols_width(\n", - # " everything() ~ px(144)\n", - # " )" + " ) |>\n", + " gt::cols_width(\n", + " everything() ~ pct(20)\n", + " ) \n" ), label = glue::glue("tbl-{tab_shortname}"), # add_option = TRUE, @@ -231,7 +235,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n } ## add table if it is long enough to be shown on >1 portrait ("reg_long") OR landscape ("wide_long") page - ### only difference: latter has landscape braces + ### only differences: latter has landscape braces and narrower cols if (tbl_class == "reg_long" | tbl_class == "wide_long") { # identify number of tables in rda load(fs::path(tables_dir, "tables", tab)) @@ -259,8 +263,9 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n " table.width = pct(100),\n", " table.layout = 'auto'\n", " ) |>\n", - # " gt::cols_width(\n", - # " everything() ~ px(144)) |>\n", + " gt::cols_width(\n", + " everything() ~ pct(20)\n", + " ) \n", " gt::gt_split(row_every_n = ", max_rows, ") |>\n", " gt::grp_pull(", i, ")\n" ), @@ -296,6 +301,11 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n plot_name = tab, essential_columns = 1 ) + + # identify number of split tables + tab <- gsub("table", "table_split", tab) + load(fs::path(tables_dir, "tables", tab)) + split_tables <- length(table_list) } # add a chunk to import split tables @@ -333,10 +343,10 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n " gt::tab_options(\n", " table.width = pct(100),\n", " table.layout = 'auto'\n", - " ) \n"#|>, - # " gt::cols_width(\n", - # " everything() ~ px(144)\n", - # " ) \n" + " ) |>\n", + " gt::cols_width(\n", + " everything() ~ pct(20)\n", + " ) \n" ), label = glue::glue("tbl-{tab_shortname}", i), add_option = TRUE, @@ -374,6 +384,11 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n plot_name = tab, essential_columns = 1 ) + + # identify number of split tables + tab <- gsub("table", "table_split", tab) + load(fs::path(tables_dir, "tables", tab)) + split_tables <- length(table_list) } # identify number of tables that each split table must be further split # into, with different rows per table @@ -417,8 +432,9 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n " table.width = pct(100),\n", " table.layout = 'auto'\n", " ) |>\n", - # " gt::cols_width(\n", - # " everything() ~ px(144)) |>\n", + " gt::cols_width(\n", + " everything() ~ pct(20)\n", + " ) \n", " gt::gt_split(row_every_n = ", max_rows, ") |>\n", " gt::grp_pull(", j, ")\n" ), From 502788f6dba77aca8aafd5d44380aac037b0dc33 Mon Sep 17 00:00:00 2001 From: sbreitbart-NOAA Date: Fri, 27 Feb 2026 13:44:35 -0500 Subject: [PATCH 21/21] Add missing pipes to create_tables_doc() --- R/create_tables_doc.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/create_tables_doc.R b/R/create_tables_doc.R index 8c51e84e..6167fdce 100644 --- a/R/create_tables_doc.R +++ b/R/create_tables_doc.R @@ -265,7 +265,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n " ) |>\n", " gt::cols_width(\n", " everything() ~ pct(20)\n", - " ) \n", + " ) |> \n", " gt::gt_split(row_every_n = ", max_rows, ") |>\n", " gt::grp_pull(", i, ")\n" ), @@ -434,7 +434,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n " ) |>\n", " gt::cols_width(\n", " everything() ~ pct(20)\n", - " ) \n", + " ) |> \n", " gt::gt_split(row_every_n = ", max_rows, ") |>\n", " gt::grp_pull(", j, ")\n" ),