Skip to content

Commit 87f23a9

Browse files
authored
Merge branch 'master' into latex_stub_separator
2 parents 14b3551 + d4829c7 commit 87f23a9

File tree

5 files changed

+148
-24
lines changed

5 files changed

+148
-24
lines changed

R/helpers.R

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1687,7 +1687,6 @@ cells_group <- function(groups = everything()) {
16871687
#' with a table that has multiple stub columns:
16881688
#'
16891689
#' ```r
1690-
#' # Create a table with multi-column stub
16911690
#' dplyr::tibble(
16921691
#' country = rep(c("USA", "Canada"), each = 3),
16931692
#' region = rep(c("North", "South", "West"), 2),
@@ -1705,11 +1704,14 @@ cells_group <- function(groups = everything()) {
17051704
#' )
17061705
#' ```
17071706
#'
1707+
#' \if{html}{\out{
1708+
#' `r man_get_image_tag(file = "man_cells_stub_2.png")`
1709+
#' }}
1710+
#'
17081711
#' You can also use content-based targeting to target rows by their actual values
17091712
#' rather than calculating row indices:
17101713
#'
17111714
#' ```r
1712-
#' # Content-based targeting example
17131715
#' gtcars |>
17141716
#' dplyr::select(mfr, model, year, hp, msrp) |>
17151717
#' dplyr::slice(1:8) |>
@@ -1724,6 +1726,10 @@ cells_group <- function(groups = everything()) {
17241726
#' )
17251727
#' ```
17261728
#'
1729+
#' \if{html}{\out{
1730+
#' `r man_get_image_tag(file = "man_cells_stub_3.png")`
1731+
#' }}
1732+
#'
17271733
#' @family location helper functions
17281734
#' @section Function ID:
17291735
#' 8-17

R/tab_footnote.R

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -703,8 +703,13 @@ set_footnote.cells_stub <- function(
703703
# vs. new usage (explicit columns parameter provided)
704704
is_traditional_usage <- is.null(loc$columns)
705705

706-
if (is_traditional_usage) {
707-
# For backward compatibility: traditional cells_stub() usage without columns parameter
706+
# For multi-column stubs, we need to apply footnotes to all stub columns
707+
# even in traditional usage mode (when columns = NULL means "all columns")
708+
has_multicolumn_stub <- length(stub_vars) > 1 && !all(is.na(stub_vars))
709+
710+
if (is_traditional_usage && !has_multicolumn_stub) {
711+
# For backward compatibility with single-column stubs: traditional
712+
# cells_stub() usage without columns parameter
708713
# Use the original "stub" locname for compatibility with existing code
709714
data <-
710715
dt_footnotes_add(
@@ -719,7 +724,8 @@ set_footnote.cells_stub <- function(
719724
)
720725
} else {
721726
# New usage: per-column stub footnotes
722-
# If no stub columns are resolved, apply to all stub columns (backward compatibility)
727+
# If no stub columns are resolved (traditional usage with multi-column stub,
728+
# or columns = NULL was explicitly provided), apply to all stub columns
723729
if (length(columns) == 0) {
724730
if (!all(is.na(stub_vars))) {
725731
columns <- stub_vars

R/tab_style.R

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,7 @@ set_style.cells_body <- function(loc, data, style) {
840840
set_style.cells_stub <- function(loc, data, style) {
841841

842842
call <- call("cells_stub")
843+
843844
resolved <- resolve_cells_stub(data = data, object = loc, call = call)
844845

845846
columns <- resolved$columns
@@ -850,10 +851,17 @@ set_style.cells_stub <- function(loc, data, style) {
850851

851852
# Check if this is traditional usage (no columns parameter)
852853
# vs. new usage (explicit columns parameter provided)
854+
853855
is_traditional_usage <- is.null(loc$columns)
854856

855-
if (is_traditional_usage) {
856-
# For backward compatibility: traditional cells_stub() usage without columns parameter
857+
# For multi-column stubs, we need to apply styles to all stub columns
858+
859+
# even in traditional usage mode (when columns = NULL means "all columns")
860+
has_multicolumn_stub <- length(stub_vars) > 1 && !all(is.na(stub_vars))
861+
862+
if (is_traditional_usage && !has_multicolumn_stub) {
863+
# For backward compatibility with single-column stubs: traditional
864+
# cells_stub() usage without columns parameter
857865
# Use the original "stub" locname for compatibility with existing code
858866
data <-
859867
dt_styles_add(
@@ -867,7 +875,8 @@ set_style.cells_stub <- function(loc, data, style) {
867875
)
868876
} else {
869877
# New usage: per-column stub styling
870-
# If no stub columns are resolved, apply to all stub columns (backward compatibility)
878+
# If no stub columns are resolved (traditional usage with multi-column stub,
879+
# or columns = NULL was explicitly provided), apply to all stub columns
871880
if (length(columns) == 0) {
872881
if (!all(is.na(stub_vars))) {
873882
columns <- stub_vars

man/cells_stub.Rd

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

tests/testthat/test-multicolumn_stub.R

Lines changed: 109 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,10 +1016,12 @@ test_that("Enhanced stub targeting: content-based targeting works", {
10161016
)
10171017

10181018
# Check that styles are applied correctly
1019+
# With multi-column stub, styles are applied to all 3 columns for each targeted row
10191020
styles <- gt:::dt_styles_get(styled_table)
1020-
expect_equal(nrow(styles), 2) # Should target 2 Ford rows
1021-
expect_equal(unique(styles$rownum), c(1, 2))
1022-
expect_equal(unique(styles$locname), "stub")
1021+
expect_equal(nrow(styles), 6) # 2 Ford rows * 3 columns
1022+
expect_setequal(unique(styles$rownum), c(1, 2))
1023+
expect_equal(unique(styles$locname), "stub_column")
1024+
expect_setequal(unique(styles$colname), c("mfr", "model", "trim"))
10231025

10241026
# Test table renders without error
10251027
expect_no_error(as_raw_html(styled_table))
@@ -1043,10 +1045,12 @@ test_that("Enhanced stub targeting: multi-value targeting works", {
10431045
)
10441046

10451047
# Check that styles are applied correctly
1048+
# With multi-column stub, styles are applied to all 3 columns for each targeted row
10461049
styles <- gt:::dt_styles_get(styled_table)
1047-
expect_equal(nrow(styles), 4) # Should target 4 rows (2 Ford + 2 BMW)
1048-
expect_equal(unique(styles$rownum), c(1, 2, 3, 4))
1049-
expect_equal(unique(styles$locname), "stub")
1050+
expect_equal(nrow(styles), 12) # 4 rows (2 Ford + 2 BMW) * 3 columns
1051+
expect_setequal(unique(styles$rownum), c(1, 2, 3, 4))
1052+
expect_equal(unique(styles$locname), "stub_column")
1053+
expect_setequal(unique(styles$colname), c("mfr", "model", "trim"))
10501054
})
10511055

10521056
test_that("Enhanced stub targeting: column-specific targeting works", {
@@ -1092,10 +1096,12 @@ test_that("Enhanced stub targeting: model-specific targeting works", {
10921096
)
10931097

10941098
# Check that styles are applied correctly
1099+
# With multi-column stub, styles are applied to all 3 columns for each targeted row
10951100
styles <- gt:::dt_styles_get(styled_table)
1096-
expect_equal(nrow(styles), 2) # Should target 2 rows (GT and A4)
1097-
expect_equal(unique(styles$rownum), c(1, 5))
1098-
expect_equal(unique(styles$locname), "stub")
1101+
expect_equal(nrow(styles), 6) # 2 rows (GT and A4) * 3 columns
1102+
expect_setequal(unique(styles$rownum), c(1, 5))
1103+
expect_equal(unique(styles$locname), "stub_column")
1104+
expect_setequal(unique(styles$colname), c("mfr", "model", "trim"))
10991105
})
11001106

11011107
test_that("Enhanced stub targeting: backward compatibility with numeric indices", {
@@ -1117,9 +1123,100 @@ test_that("Enhanced stub targeting: backward compatibility with numeric indices"
11171123

11181124
# Check that styles are applied correctly
11191125
styles <- gt:::dt_styles_get(styled_table)
1120-
expect_equal(nrow(styles), 3) # Should target 3 rows
1121-
expect_equal(unique(styles$rownum), c(1, 3, 5))
1122-
expect_equal(unique(styles$locname), "stub")
1126+
# With multi-column stub, styles should be applied to all 3 columns for each of 3 rows
1127+
expect_equal(nrow(styles), 9) # 3 rows * 3 columns
1128+
expect_setequal(unique(styles$rownum), c(1, 3, 5))
1129+
expect_equal(unique(styles$locname), "stub_column")
1130+
expect_setequal(unique(styles$colname), c("mfr", "model", "trim"))
1131+
})
1132+
1133+
test_that("cells_stub(columns = NULL) targets all stub columns", {
1134+
1135+
# `cells_stub(columns = NULL)` should target all stub columns in a multi-column stub
1136+
# Test with explicit columns = NULL
1137+
styled_table_null <-
1138+
exibble |>
1139+
gt(rowname_col = c("group", "char")) |>
1140+
tab_style(
1141+
style = cell_text(align = "right"),
1142+
locations = cells_stub(columns = NULL)
1143+
)
1144+
1145+
# Check that styles are applied to both stub columns
1146+
styles_null <- gt:::dt_styles_get(styled_table_null)
1147+
expect_equal(unique(styles_null$locname), "stub_column")
1148+
expect_setequal(unique(styles_null$colname), c("group", "char"))
1149+
expect_equal(nrow(styles_null), 16) # 8 rows * 2 columns
1150+
1151+
# Test with default (no columns argument)
1152+
styled_table_default <-
1153+
exibble |>
1154+
gt(rowname_col = c("group", "char")) |>
1155+
tab_style(
1156+
style = cell_fill(color = "steelblue"),
1157+
locations = cells_stub()
1158+
)
1159+
1160+
# Check that styles are applied to both stub columns
1161+
styles_default <- gt:::dt_styles_get(styled_table_default)
1162+
expect_equal(unique(styles_default$locname), "stub_column")
1163+
expect_setequal(unique(styles_default$colname), c("group", "char"))
1164+
expect_equal(nrow(styles_default), 16) # 8 rows * 2 columns
1165+
1166+
# Check that rendered HTML contains the styles for both columns
1167+
html_output <- as_raw_html(styled_table_default)
1168+
# The style should be applied to multiple stub cells (not just the first column)
1169+
expect_true(grepl("#4682B4", html_output)) # steelblue
1170+
1171+
# Compare with single-column stub (backward compatibility)
1172+
styled_table_single <-
1173+
sza |>
1174+
dplyr::filter(latitude == 20 & tst <= "1000") |>
1175+
dplyr::select(-latitude) |>
1176+
dplyr::filter(!is.na(sza)) |>
1177+
tidyr::pivot_wider(
1178+
names_from = "tst",
1179+
values_from = sza,
1180+
names_sort = TRUE
1181+
) |>
1182+
gt(rowname_col = "month") |>
1183+
sub_missing(missing_text = "") |>
1184+
tab_style(
1185+
style = list(
1186+
cell_fill(color = "darkblue"),
1187+
cell_text(color = "white")
1188+
),
1189+
locations = cells_stub()
1190+
)
1191+
1192+
# Single-column stub should use "stub" locname for backward compatibility
1193+
styles_single <- gt:::dt_styles_get(styled_table_single)
1194+
expect_equal(unique(styles_single$locname), "stub")
1195+
})
1196+
1197+
test_that("cells_stub() footnotes target all stub columns in multi-column stub", {
1198+
1199+
# cells_stub() should apply footnotes to all stub columns in a multi-column stub
1200+
footnoted_table <-
1201+
head(exibble, 4) |>
1202+
gt(rowname_col = c("group", "char")) |>
1203+
tab_footnote(
1204+
footnote = "Test",
1205+
locations = cells_stub()
1206+
)
1207+
1208+
# Check that footnotes are applied to both stub columns
1209+
footnotes <- gt:::dt_footnotes_get(footnoted_table)
1210+
expect_setequal(unique(footnotes$colname), c("group", "char"))
1211+
expect_equal(nrow(footnotes), 8)
1212+
# 4 rows * 2 columns
1213+
1214+
# Check that rendered HTML contains footnote marks in all stub cells
1215+
html_output <- as_raw_html(footnoted_table)
1216+
1217+
# The footnote mark "1" should appear multiple times (once per stub cell)
1218+
# Count occurrences of the footnote mark class in stub cells
1219+
expect_true(grepl("Test", html_output)) # Footnote text is present
11231220
})
11241221

11251222
test_that("Enhanced stub targeting: error handling for invalid targets", {

0 commit comments

Comments
 (0)