diff --git a/NEWS.md b/NEWS.md index 94f9bc2e42..e24ba076c8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,8 @@ * `opt_interactive()` now shows row names if `rownames_to_stub = TRUE` (@olivroy, #1702). +* `opt_ineractive()` now displays the stub header label created with `tab_stubhead()` (@olivroy, #1758). + * `data_color()` throws a more informative error message if `rows` didn't resolve to anything (@olivroy, #1659). * PDF output now allows the font size of a table to be set using the table.font.size parameter in the tab_options function (#1472). The font sizes of individual table cells (including those in the body, stubs, column headings, etc.) can be set using tab_style function. Several other options specified in tab_style are now reflected in PDF output. diff --git a/R/render_as_i_html.R b/R/render_as_i_html.R index 336487cf05..8547901651 100644 --- a/R/render_as_i_html.R +++ b/R/render_as_i_html.R @@ -55,74 +55,51 @@ render_as_ihtml <- function(data, id) { # Obtain the language from the `locale`, if provided locale <- normalize_locale(dt_locale_get_value(data = data)) - # Generate a `lang_defs` object to pass to the `language` argument - if (is.null(locale) || locale == "en") { - - lang_defs <- reactable::reactableLang() + lang_defs <- get_ihtml_translations(locale) + column_groups <- dt_boxhead_get_vars_groups(data = data) + # if dt_boxhead_get_vars_groups is fixed, this will no longer be necessary. + if (identical(column_groups, NA_character_)) { + column_groups <- NULL + } + rownames_to_stub <- stub_rownames_has_column(data) + # value to use for rowname_col or groupname_col title + # Will use it for rowname_col only if groupname_col is undefined. + # Will use it only for the first groupname_col. + stub_label <- dt_stubhead_get(data)$label + + if (is.null(stub_label)) { + # No label if stubhead label is undefined. + rowname_label <- "" + groupname_label <- "" + } else if (!is.null(column_groups)) { + rowname_label <- "" + groupname_label <- stub_label } else { - - locale_data <- locales[locales$locale == locale, ][1, ] - - if (is.na(locale_data[["no_table_data_text"]])) { - - lang_defs <- reactable::reactableLang() - - } else { - - lang_defs <- - reactable::reactableLang( - sortLabel = locale_data[["sort_label_text"]], - filterPlaceholder = "", - filterLabel = locale_data[["filter_label_text"]], - searchPlaceholder = locale_data[["search_placeholder_text"]], - searchLabel = locale_data[["search_placeholder_text"]], - noData = locale_data[["no_table_data_text"]], - pageNext = locale_data[["page_next_text"]], - pagePrevious = locale_data[["page_previous_text"]], - pageNumbers = locale_data[["page_numbers_text"]], - pageInfo = gsub("\\\\u2013", "\u2013", locale_data[["page_info_text"]]), - pageSizeOptions = locale_data[["page_size_options_text"]], - pageNextLabel = locale_data[["page_next_label_text"]], - pagePreviousLabel = locale_data[["page_previous_label_text"]], - pageNumberLabel = locale_data[["page_number_label_text"]], - pageJumpLabel = locale_data[["page_jump_label_text"]], - pageSizeOptionsLabel = locale_data[["page_size_options_label_text"]], - groupExpandLabel = "Toggle group", - detailsExpandLabel = "Toggle details", - selectAllRowsLabel = "Select all rows", - selectAllSubRowsLabel = "Select all rows in group", - selectRowLabel = "Select row" - ) - } + rowname_label <- stub_label + groupname_label <- NULL } + # Obtain the underlying data table (including group rows) data_tbl0 <- dt_data_get(data = data) - # # Only preserve columns that are not hidden (group cols will be added later) - # - data_tbl_vars <- dt_boxhead_get_vars_default(data = data) data_tbl <- data_tbl0[, data_tbl_vars, drop = FALSE] #nocov start - - # Stop function if there are no visible columns if (ncol(data_tbl) < 1) { - + # Stop function if there are no visible columns cli::cli_abort(c( "When displaying an interactive gt table, there must be at least one visible column.", "*" = "Check that the input data table has at least one column,", "*" = "Failing that, look at whether all columns have been inadvertently hidden." )) } - #nocov end - rownames_to_stub <- stub_rownames_has_column(data) # use of special .rownames doesn't work. # Workaround https://github.com/glin/reactable/issues/378 # rstudio/gt#1702 @@ -130,19 +107,28 @@ render_as_ihtml <- function(data, id) { rowname_col <- dt_boxhead_get_var_stub(data) if (length(rowname_col) == 1) { # avoid base R error when setting duplicate row names. - attr(data_tbl, "row.names") <- as.character(data$`_data`[[rowname_col]]) + row_names <- as.character(data$`_data`[[rowname_col]]) + # Convert to NA string to avoid wrong output. + # TODO figure out if there is a way to get the sub_missing value. + # With data$`_substitutions` + row_names <- dplyr::coalesce(row_names, "") + attr(data_tbl, "row.names") <- row_names + row_name_col_def <- list(reactable::colDef( + name = rowname_label + # TODO pass on other attributes of row names column if necessary. + )) + # Create colDef row name with special ".rownames" from reactable. + names(row_name_col_def) <- ".rownames" + } + } else { + row_name_col_def <- NULL } # Obtain column label attributes column_names <- dt_boxhead_get_vars_default(data = data) column_labels <- dt_boxhead_get_vars_labels_default(data = data) column_alignments <- dt_boxhead_get_vars_align_default(data = data) - column_groups <- dt_boxhead_get_vars_groups(data = data) - # if dt_boxhead_get_vars_groups is fixed, this will no longer be necessary. - if (identical(column_groups, NA_character_)) { - column_groups <- NULL - } # Obtain widths for each visible column label boxh <- dt_boxhead_get(data = data) @@ -307,37 +293,47 @@ render_as_ihtml <- function(data, id) { return cellInfo.value }") for (i in seq_along(column_groups)) { - group_col_defs[[i]] <- reactable::colDef( - name = "", - grouped = grp_fn, - # FIXME Should groups be sticky? (or provide a way to do this) - sticky = NULL + if (i == 1) { + # Use the stubhead label for the first group + group_label <- groupname_label + } else { + # by default, don't name groupname_col for consistency with non-interactive + group_label <- "" + } + + group_col_defs[[i]] <- + reactable::colDef( + name = group_label, + # The total number of rows is wrong in colGroup, possibly due to the JS fn + grouped = grp_fn, + # FIXME Should groups be sticky? (or provide a way to do this) + sticky = NULL ) } names(group_col_defs) <- column_groups - # Add group colDef to general col_def - col_defs <- c(col_defs, group_col_defs) + groupname_col <- column_groups # for defaultExpanded = TRUE expand_groupname_col <- TRUE - # modify data_tbl to include data_tbl <- dplyr::bind_cols( data_tbl, data_tbl0[ , groupname_col, drop = FALSE] ) - # Set number of rows to FALSE, since it is inacurrate - # Otherwise, it just shows the number of groups } else { groupname_col <- NULL + group_col_defs <- NULL expand_groupname_col <- FALSE } # # Generate custom styles for `defaultColDef` # + # Add group colDef and rowname colDef to general col_def + col_defs <- c(col_defs, group_col_defs, row_name_col_def) + styles_tbl <- dt_styles_get(data = data) body_styles_tbl <- dplyr::filter(styles_tbl, locname %in% c("data", "stub")) body_styles_tbl <- dplyr::arrange(body_styles_tbl, colnum, rownum) @@ -378,6 +374,7 @@ render_as_ihtml <- function(data, id) { collapse = "" ) + # TODO if `sub_missing()` is enabled gloablly, just use `na = ` here! default_col_def <- reactable::colDef( style = reactable::JS(body_style_js_str), @@ -808,3 +805,47 @@ create_footnotes_component_ihtml <- function(data) { ) ) } + +get_ihtml_translations <- function(locale) { + if (is.null(locale) || locale == "en") { + + lang_defs <- reactable::reactableLang() + + } else { + + locale_data <- locales[locales$locale == locale, ][1, ] + + if (is.na(locale_data[["no_table_data_text"]])) { + + lang_defs <- reactable::reactableLang() + + } else { + + lang_defs <- + reactable::reactableLang( + sortLabel = locale_data[["sort_label_text"]], + filterPlaceholder = "", + filterLabel = locale_data[["filter_label_text"]], + searchPlaceholder = locale_data[["search_placeholder_text"]], + searchLabel = locale_data[["search_placeholder_text"]], + noData = locale_data[["no_table_data_text"]], + pageNext = locale_data[["page_next_text"]], + pagePrevious = locale_data[["page_previous_text"]], + pageNumbers = locale_data[["page_numbers_text"]], + pageInfo = gsub("\\\\u2013", "\u2013", locale_data[["page_info_text"]]), + pageSizeOptions = locale_data[["page_size_options_text"]], + pageNextLabel = locale_data[["page_next_label_text"]], + pagePreviousLabel = locale_data[["page_previous_label_text"]], + pageNumberLabel = locale_data[["page_number_label_text"]], + pageJumpLabel = locale_data[["page_jump_label_text"]], + pageSizeOptionsLabel = locale_data[["page_size_options_label_text"]], + groupExpandLabel = "Toggle group", + detailsExpandLabel = "Toggle details", + selectAllRowsLabel = "Select all rows", + selectAllSubRowsLabel = "Select all rows in group", + selectRowLabel = "Select row" + ) + } + } + lang_defs +} diff --git a/tests/testthat/test-i_html.R b/tests/testthat/test-i_html.R index 5256d958ed..1055f37491 100644 --- a/tests/testthat/test-i_html.R +++ b/tests/testthat/test-i_html.R @@ -80,7 +80,16 @@ test_that("Interactive tables won't fail when using different options", { tbl_gt_i_25 <- gt(mtcars_short, groupname_col = "vs") %>% opt_interactive() - + # #1758 tab_stubhead() respected (shown on top of group) + tbl_gt_i_26 <- + gt(mtcars_short, groupname_col = "vs", rownames_to_stub = T) %>% + tab_stubhead("stub label on top of vs") %>% + opt_interactive() + # #1758 NA rows show correctly + stubhead shows on top of row + tbl_gt_i_27 <- + gt(exibble, rowname_col = "char") %>% + tab_stubhead("stub label on top of rowname") %>% + opt_interactive() capture_output(expect_no_error(tbl_gt_i_01)) capture_output(expect_no_error(tbl_gt_i_02)) capture_output(expect_no_error(tbl_gt_i_03)) @@ -106,4 +115,7 @@ test_that("Interactive tables won't fail when using different options", { capture_output(expect_no_error(tbl_gt_i_23)) capture_output(expect_no_error(tbl_gt_i_24)) capture_output(expect_no_error(tbl_gt_i_25)) + capture_output(expect_no_error(tbl_gt_i_26)) + capture_output(expect_no_error(tbl_gt_i_27)) + }) diff --git a/tests/testthat/test-substitution.R b/tests/testthat/test-substitution.R index 00bb32897c..02f1719b46 100644 --- a/tests/testthat/test-substitution.R +++ b/tests/testthat/test-substitution.R @@ -26,7 +26,7 @@ test_that("sub_missing() works correctly", { # Expect an error when attempting to format a column # that does not exist - expect_error(tab %>% sub_missing(columns = "num_3")) + expect_error(sub_missing(tab, columns = "num_3")) expect_equal( (tab %>%