From 64535354b79b0ebe5fbb0d340da9f78a7990315a Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 24 Jun 2025 16:16:06 +0200 Subject: [PATCH 01/12] Add some helper function to create data-qmd attributes This is useful for span in tables --- DESCRIPTION | 1 + NAMESPACE | 10 ++ R/quarto-package.R | 4 + R/table-helper.R | 213 ++++++++++++++++++++++++++++++++++++++++ man/tbl_qmd_elements.Rd | 107 ++++++++++++++++++++ 5 files changed, 335 insertions(+) create mode 100644 R/table-helper.R create mode 100644 man/tbl_qmd_elements.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 8be291b3..356dff5f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,6 +20,7 @@ Depends: Imports: cli, fs, + htmltools, jsonlite, later, processx, diff --git a/NAMESPACE b/NAMESPACE index 69eeb326..6cfc352c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -20,6 +20,12 @@ export(quarto_serve) export(quarto_update_extension) export(quarto_use_template) export(quarto_version) +export(tbl_qmd_div) +export(tbl_qmd_div_base64) +export(tbl_qmd_div_raw) +export(tbl_qmd_span) +export(tbl_qmd_span_base64) +export(tbl_qmd_span_raw) export(theme_brand_flextable) export(theme_brand_ggplot2) export(theme_brand_gt) @@ -34,16 +40,20 @@ export(write_yaml_metadata_block) import(rlang) importFrom(cli,cli_abort) importFrom(cli,cli_inform) +importFrom(htmltools,div) +importFrom(htmltools,span) importFrom(jsonlite,fromJSON) importFrom(later,later) importFrom(processx,process) importFrom(processx,run) +importFrom(rlang,caller_env) importFrom(rlang,is_interactive) importFrom(rmarkdown,relative_to) importFrom(rstudioapi,isAvailable) importFrom(rstudioapi,viewer) importFrom(tools,vignetteEngine) importFrom(utils,browseURL) +importFrom(xfun,base64_encode) importFrom(xfun,env_option) importFrom(yaml,as.yaml) importFrom(yaml,write_yaml) diff --git a/R/quarto-package.R b/R/quarto-package.R index f8cb26cf..660855c6 100644 --- a/R/quarto-package.R +++ b/R/quarto-package.R @@ -4,7 +4,11 @@ ## usethis namespace: start #' @import rlang #' @importFrom cli cli_inform +#' @importFrom htmltools div +#' @importFrom htmltools span +#' @importFrom rlang caller_env #' @importFrom tools vignetteEngine +#' @importFrom xfun base64_encode #' @importFrom xfun env_option ## usethis namespace: end NULL diff --git a/R/table-helper.R b/R/table-helper.R new file mode 100644 index 00000000..4376dfc4 --- /dev/null +++ b/R/table-helper.R @@ -0,0 +1,213 @@ +#' Create Quarto Markdown HTML Elements for Tables +#' +#' Functions to wrap content in HTML spans or divs with data-qmd attributes for +#' Quarto processing within HTML tables. These functions are specifically designed +#' for use with HTML table packages like kableExtra, gt, or DT where you need +#' Quarto to process markdown content within table cells. +#' +#' @details +#' These functions create HTML elements with `data-qmd` or `data-qmd-base64` +#' attributes that Quarto processes during document rendering. The base64 +#' encoding is recommended for content with special characters, quotes, or +#' complex formatting. +#' +#' Available functions: +#' +#' * `tbl_qmd_span()` and `tbl_qmd_div()` are the main functions with encoding options +#' * `tbl_qmd_span_base64()` and `tbl_qmd_div_base64()` explicitly use base64 encoding +#' * `tbl_qmd_span_raw()` and `tbl_qmd_div_raw()` explicitly use raw encoding +#' +#' This feature requires Quarto version 1.3 or higher with HTML format outputs. +#' For more information, see . +#' +#' @param content Character string of content to wrap. This can include Markdown, +#' LaTeX math, and Quarto shortcodes. +#' @param display Optional display text (if different from content). Useful for +#' fallback text when Quarto processing is not available or for better +#' accessibility. +#' @param use_base64 Logical, whether to base64 encode the content (recommended +#' for complex content with special characters or when content includes quotes) +#' @param class Optional CSS class(es) to add to the element. While this works for +#' both span and div elements, it's more commonly used with div elements. +#' @param attrs Named list of additional HTML attributes to add to the element. +#' For example: `list(id = "my-element", title = "Tooltip text")` +#' +#' @return Character string containing the HTML element with appropriate data-qmd attributes +#' +#' @examples +#' # Basic span usage in table cells +#' tbl_qmd_span("**bold text**") +#' tbl_qmd_span("$\\alpha + \\beta$", display = "Greek formula") +#' +#' # Basic div usage in table cells +#' tbl_qmd_div("## Section Title\n\nContent here") +#' tbl_qmd_div("{{< video https://example.com >}}", display = "[Video content]") +#' +#' # Explicit encoding choices +#' tbl_qmd_span_base64("Complex $\\LaTeX$ content") +#' tbl_qmd_span_raw("Simple text") +#' +#' # Use with custom attributes +#' tbl_qmd_span("**Important note**", attrs = list(title = "Hover for more info")) +#' tbl_qmd_div("Content here", class = "callout", +#' attrs = list(id = "special-note", tabindex = "0")) +#' +#' # Use with different HTML table packages +#' \dontrun{ +#' # With kableExtra +#' library(kableExtra) +#' df <- data.frame( +#' math = c(tbl_qmd_span("$x^2$"), tbl_qmd_span("$\\sum_{i=1}^n x_i$")), +#' text = c(tbl_qmd_span("**Important**", "bold"), tbl_qmd_span("`code`", "code")) +#' ) +#' kbl(df, format = "html", escape = FALSE) |> kable_styling() +#' } +#' @name tbl_qmd_elements +NULL + + +.validate_tbl_qmd_input <- function( + content, + display = NULL, + call = rlang::caller_env() +) { + if (!is.character(content) || length(content) != 1) { + cli::cli_abort("'content' must be a single character string", call = call) + } + + if (!is.null(display) && (!is.character(display) || length(display) != 1)) { + cli::cli_abort( + "'display' must be NULL or a single character string", + call = call + ) + } + + invisible(TRUE) +} + + +.tbl_qmd_element <- function( + tag, + content, + display, + use_base64, + class = NULL, + attrs = NULL +) { + .validate_tbl_qmd_input(content, display) + + if (is.null(display)) { + display <- content + } + + if (use_base64) { + encoded_content <- xfun::base64_encode(charToRaw(content)) + attr_list <- list("data-qmd-base64" = encoded_content) + } else { + attr_list <- list("data-qmd" = content) + } + + # Add class if provided + if (!is.null(class)) { + attr_list$class <- class + } + + # Add any additional attributes + if (!is.null(attrs) && is.list(attrs) && length(attrs) > 0) { + attr_list <- c(attr_list, attrs) + } + # Create HTML element using htmltools + html_element <- if (tag == "div") { + do.call(htmltools::div, c(list(display, .noWS = "outside"), attr_list)) + } else { + do.call(htmltools::span, c(list(display, .noWS = "outside"), attr_list)) + } + + # Convert to character string + as.character(html_element) +} + +#' @rdname tbl_qmd_elements +#' @export +tbl_qmd_span <- function( + content, + display = NULL, + use_base64 = TRUE, + class = NULL, + attrs = NULL +) { + .tbl_qmd_element("span", content, display, use_base64, class, attrs) +} + +#' @rdname tbl_qmd_elements +#' @export +tbl_qmd_div <- function( + content, + display = NULL, + use_base64 = TRUE, + class = NULL, + attrs = NULL +) { + .tbl_qmd_element("div", content, display, use_base64, class, attrs) +} +#' @rdname tbl_qmd_elements +#' @export +tbl_qmd_span_base64 <- function( + content, + display = NULL, + class = NULL, + attrs = NULL +) { + tbl_qmd_span( + content, + display, + use_base64 = TRUE, + class = class, + attrs = attrs + ) +} + +#' @rdname tbl_qmd_elements +#' @export +tbl_qmd_div_base64 <- function( + content, + display = NULL, + class = NULL, + attrs = NULL +) { + tbl_qmd_div(content, display, use_base64 = TRUE, class = class, attrs = attrs) +} + +#' @rdname tbl_qmd_elements +#' @export +tbl_qmd_span_raw <- function( + content, + display = NULL, + class = NULL, + attrs = NULL +) { + tbl_qmd_span( + content, + display, + use_base64 = FALSE, + class = class, + attrs = attrs + ) +} + +#' @rdname tbl_qmd_elements +#' @export +tbl_qmd_div_raw <- function( + content, + display = NULL, + class = NULL, + attrs = NULL +) { + tbl_qmd_div( + content, + display, + use_base64 = FALSE, + class = class, + attrs = attrs + ) +} diff --git a/man/tbl_qmd_elements.Rd b/man/tbl_qmd_elements.Rd new file mode 100644 index 00000000..b5b97f2d --- /dev/null +++ b/man/tbl_qmd_elements.Rd @@ -0,0 +1,107 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/table-helper.R +\name{tbl_qmd_elements} +\alias{tbl_qmd_elements} +\alias{tbl_qmd_span} +\alias{tbl_qmd_div} +\alias{tbl_qmd_span_base64} +\alias{tbl_qmd_div_base64} +\alias{tbl_qmd_span_raw} +\alias{tbl_qmd_div_raw} +\title{Create Quarto Markdown HTML Elements for Tables} +\usage{ +tbl_qmd_span( + content, + display = NULL, + use_base64 = TRUE, + class = NULL, + attrs = NULL +) + +tbl_qmd_div( + content, + display = NULL, + use_base64 = TRUE, + class = NULL, + attrs = NULL +) + +tbl_qmd_span_base64(content, display = NULL, class = NULL, attrs = NULL) + +tbl_qmd_div_base64(content, display = NULL, class = NULL, attrs = NULL) + +tbl_qmd_span_raw(content, display = NULL, class = NULL, attrs = NULL) + +tbl_qmd_div_raw(content, display = NULL, class = NULL, attrs = NULL) +} +\arguments{ +\item{content}{Character string of content to wrap. This can include Markdown, +LaTeX math, and Quarto shortcodes.} + +\item{display}{Optional display text (if different from content). Useful for +fallback text when Quarto processing is not available or for better +accessibility.} + +\item{use_base64}{Logical, whether to base64 encode the content (recommended +for complex content with special characters or when content includes quotes)} + +\item{class}{Optional CSS class(es) to add to the element. While this works for +both span and div elements, it's more commonly used with div elements.} + +\item{attrs}{Named list of additional HTML attributes to add to the element. +For example: \code{list(id = "my-element", title = "Tooltip text")}} +} +\value{ +Character string containing the HTML element with appropriate data-qmd attributes +} +\description{ +Functions to wrap content in HTML spans or divs with data-qmd attributes for +Quarto processing within HTML tables. These functions are specifically designed +for use with HTML table packages like kableExtra, gt, or DT where you need +Quarto to process markdown content within table cells. +} +\details{ +These functions create HTML elements with \code{data-qmd} or \code{data-qmd-base64} +attributes that Quarto processes during document rendering. The base64 +encoding is recommended for content with special characters, quotes, or +complex formatting. + +Available functions: +\itemize{ +\item \code{tbl_qmd_span()} and \code{tbl_qmd_div()} are the main functions with encoding options +\item \code{tbl_qmd_span_base64()} and \code{tbl_qmd_div_base64()} explicitly use base64 encoding +\item \code{tbl_qmd_span_raw()} and \code{tbl_qmd_div_raw()} explicitly use raw encoding +} + +This feature requires Quarto version 1.3 or higher with HTML format outputs. +For more information, see \url{https://quarto.org/docs/authoring/tables.html#html-tables}. +} +\examples{ +# Basic span usage in table cells +tbl_qmd_span("**bold text**") +tbl_qmd_span("$\\\\alpha + \\\\beta$", display = "Greek formula") + +# Basic div usage in table cells +tbl_qmd_div("## Section Title\n\nContent here") +tbl_qmd_div("{{< video https://example.com >}}", display = "[Video content]") + +# Explicit encoding choices +tbl_qmd_span_base64("Complex $\\\\LaTeX$ content") +tbl_qmd_span_raw("Simple text") + +# Use with custom attributes +tbl_qmd_span("**Important note**", attrs = list(title = "Hover for more info")) +tbl_qmd_div("Content here", class = "callout", + attrs = list(id = "special-note", tabindex = "0")) + +# Use with different HTML table packages +\dontrun{ +# With kableExtra +library(kableExtra) +df <- data.frame( + math = c(tbl_qmd_span("$x^2$"), tbl_qmd_span("$\\\\sum_{i=1}^n x_i$")), + text = c(tbl_qmd_span("**Important**", "bold"), tbl_qmd_span("`code`", "code")) +) +kbl(df, format = "html", escape = FALSE) |> kable_styling() +} +} From bb65cda999f329ebc8be050ca24841a9212eaa1c Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 24 Jun 2025 16:37:27 +0200 Subject: [PATCH 02/12] Add tests --- tests/testthat/test-table-helper.R | 111 +++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 tests/testthat/test-table-helper.R diff --git a/tests/testthat/test-table-helper.R b/tests/testthat/test-table-helper.R new file mode 100644 index 00000000..1c099596 --- /dev/null +++ b/tests/testthat/test-table-helper.R @@ -0,0 +1,111 @@ +test_that("tbl_qmd_span generates correct HTML with base64 encoding", { + expect_match( + tbl_qmd_span("**bold text**"), + "**bold text**", + fixed = TRUE + ) + expect_match( + tbl_qmd_span("$\\alpha + \\beta$", display = "Greek formula"), + "Greek formula", + fixed = TRUE + ) + expect_match( + tbl_qmd_span("**bold text**", class = "highlight"), + "**bold text**", + fixed = TRUE + ) + expect_match( + tbl_qmd_span( + "**Important note**", + attrs = list(title = "Hover for more info") + ), + "**Important note**", + fixed = TRUE + ) +}) + +test_that("tbl_qmd_span_raw generates correct HTML with raw encoding", { + expect_match( + tbl_qmd_span_raw("Simple text"), + "Simple text", + fixed = TRUE + ) +}) + +test_that("tbl_qmd_div generates correct HTML with base64 encoding", { + # Test with default base64 encoding + expect_match( + tbl_qmd_div("## Section Title\n\nContent here"), + "
## Section Title\n\nContent here
", + fixed = TRUE + ) + + # Test with display text + expect_match( + tbl_qmd_div( + "{{< video https://example.com >}}", + display = "[Video content]" + ), + "
[Video content]
", + fixed = TRUE + ) + + expect_match( + tbl_qmd_div("Content here", class = "something"), + "
Content here
", + fixed = TRUE + ) + expect_match( + tbl_qmd_div( + "Content here", + attrs = list(id = "special-note", tabindex = "0") + ), + "
Content here
", + fixed = TRUE + ) +}) + +test_that("tbl_qmd_div_raw generates correct HTML with raw encoding", { + result <- tbl_qmd_div_raw("## Simple header") + expect_true(grepl("
display", + fixed = TRUE + ) + + expect_match( + .tbl_qmd_element("div", "content", "display", TRUE), + "
display
", + fixed = TRUE + ) +}) From cd83129432cf412b6aa5557c277d48f667be76aa Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 24 Jun 2025 17:17:31 +0200 Subject: [PATCH 03/12] Add function to pkgdown --- _pkgdown.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/_pkgdown.yml b/_pkgdown.yml index 817d12d0..d006d952 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -56,6 +56,12 @@ reference: - starts_with("theme_colors") - starts_with("theme_brand") +- title: "Table Helpers" + desc: > + These functions are used to help with tables in Quarto documents: + contents: + - starts_with("tbl_qmd_") + - title: "Miscellaneous" desc: > These functions are used to help with Quarto documents and projects: From 5c54de99b0452c7614ff29f84538a185a52bd7f9 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 24 Jun 2025 20:35:46 +0200 Subject: [PATCH 04/12] first iteration on a vignette --- DESCRIPTION | 5 +- vignettes/markdown-html-tables.qmd | 218 +++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 vignettes/markdown-html-tables.qmd diff --git a/DESCRIPTION b/DESCRIPTION index 356dff5f..fdc06263 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -41,6 +41,7 @@ Suggests: ggplot2, gt, heatmaply, + kableExtra, knitr, palmerpenguins, patchwork, @@ -50,8 +51,8 @@ Suggests: testthat (>= 3.1.7), thematic, tidyverse, - withr, - whoami + whoami, + withr VignetteBuilder: quarto Config/testthat/edition: 3 diff --git a/vignettes/markdown-html-tables.qmd b/vignettes/markdown-html-tables.qmd new file mode 100644 index 00000000..34d02ad6 --- /dev/null +++ b/vignettes/markdown-html-tables.qmd @@ -0,0 +1,218 @@ +--- +title: "Using Markdown in HTML Tables" +format: + html: + toc: true + toc-depth: 3 +keep-md: true +vignette: > + %\VignetteIndexEntry{Using Markdown in HTML Tables} + %\VignetteEngine{quarto::html} + %\VignetteEncoding{UTF-8} +--- + +## Introduction + +Quarto allows you to include Markdown syntax inside HTML tables, making it possible to add formatting, links, images, and even more complex elements like videos to your table cells. This vignette demonstrates how to use the table helper functions provided by this package to simplify this process. + +The main challenge when working with Markdown in HTML tables is that Quarto won't automatically process Markdown content. Quarto addresses this using special `data-qmd` attributes that tell the Quarto processor to interpret the content as Markdown. This package provides helper functions to create these attributes easily. + +See Quarto documentation about HTML tables parsing: . + +## Basic Usage + +The table helper functions create HTML elements (`` or `
`) with the appropriate `data-qmd` or `qmd-base64` attributes. There are two main types of functions: + +1. Functions for creating `` elements: + - main function is `tbl_qmd_span()`, defaulting to base64 encoding, + - Two others are explicit versions: `tbl_qmd_span_base64()` and `tbl_qmd_span_raw()` +2. Functions for creating `
` elements: + - main function is `tbl_qmd_div()`, defaulting to base64 encoding, + - Two others are explicit versions: `tbl_qmd_div_base64()` and `tbl_qmd_div_raw()` + +Base64 encoding is useful when your Markdown content contains special characters or HTML tags, and this is used by default to avoid any escaping problems using this feature. + +### Example with a Basic HTML Table + +Here's a simple example of creating an HTML table with Markdown content: + +```{r} +library(quarto) + +# Create a simple data frame +data <- data.frame( + Column1 = c("Row 1", "Row 2", "Row 3"), + Column2 = c("Value 1", "Value 2", "Value 3") +) + +# Function to add Markdown formatting to table cells +add_markdown <- function(data) { + data$Column1 <- sapply(data$Column1, function(x) { + tbl_qmd_span(paste0("**", x, "**")) + }) + data$Column2 <- sapply(data$Column2, function(x) { + tbl_qmd_span(paste0("*", x, "*")) + }) + return(data) +} + +# Apply Markdown formatting +data_with_md <- add_markdown(data) + +# Display the data frame as an HTML table +knitr::kable(data_with_md, format = "html", escape = FALSE) +``` + +### Using with knitr::kable() + +The `knitr::kable()` function is a common way to create tables in R Markdown and Quarto. By setting `escape = FALSE`, we can include HTML in the table cells: + +```{r} +#| label: tbl-kable-equation +#| tbl-cap: A table with a math equation rendered using Quarto's data-qmd attribute +library(quarto) + +# Create a data frame with math expressions +tbl <- data.frame( + var = c("$a$", "$b$", "$c$"), + val = c(1, 2, 3) +) + +# Add data-qmd attributes to the math expressions +tbl$var <- sapply(tbl$var, tbl_qmd_span) + +# Create the table +knitr::kable(tbl, format = "html", escape = FALSE) +``` + +## Advanced Features + +### Display Text + +Some feature are Quarto features only. If your table can be used in other context than Quarto, you might want to use the `display` argument to provide a text that will be shown in the table instead of the Markdown content, as it won't be processed outside of Quarto documents. + +For example, you might want to show a placeholder when using video shortcodes in a table, as the video player won't be rendered outside of Quarto: + +```{r} +#| label: video-placeholder +# Create a video embed with a display text +video_embed <- tbl_qmd_span( + "{{< video https://www.youtube.com/embed/wo9vZccmqwc >}}", + display = "[Video Player]" +) + +# Create a data frame with the video embed +data <- data.frame( + Content = c("Regular text", video_embed), + Description = c("Just some text", "A YouTube video") +) + +# Create the table +knitr::kable(data, format = "html", escape = FALSE) +``` + +Behavior when the table is not processed by Quarto is simulated by opting-out html table processing for this specific table. For example, when `html-table-processing: none` cell option is set like in the Quarto computation cell below. + +```{r} +#| label: video-placeholder +#| echo: fenced +#| html-table-processing: none +``` + +Output above is a HTML table not processed by Quarto, so the video shortcode is not rendered as a video player, but as a regular text. + +See more about disabling HTML table processing in the [Quarto documentation](https://quarto.org/docs/authoring/tables.html#disabling-quarto-table-processing). + +::: {.callout-important} + +## Limitations + +Using `data-qmd` or `data-qmd-base64` attributes is a Quarto-specific feature and it will only be working when Quarto is allowed to process HTML tables. If this is used in an environment or a document that do opt-out Quarto HTML table processing, the content will not be rendered as expected. + +::: + +## Table package integration + +### Using with kableExtra + +Here's a more complex example that combines all these features to create a complete HTML table with Markdown content: + +```{r} +library(quarto) +library(kableExtra) + +# Create a data frame with different types of content +complex_table <- data.frame( + Feature = c("Formatting", "Math", "References", "Media"), + Example = c( + tbl_qmd_span("**Bold**, *italic*, and `code`"), + tbl_qmd_span("$\\int_{a}^{b} f(x) \\, dx$"), + tbl_qmd_span("See @tbl-kable-equation for example of a table"), + tbl_qmd_div( + "{{< video https://www.youtube.com/embed/wo9vZccmqwc >}}", + display = "[Video Player]" + ) + ), + Notes = c( + "Basic markdown formatting", + "LaTeX math expressions", + "Cross-references to other document elements", + "Embedded media using shortcodes" + ) +) + +# Create and style the table +kbl(complex_table, format = "html", escape = FALSE) %>% + kable_classic() %>% + column_spec(2, width = "40%") %>% + row_spec(0, bold = TRUE, background = "#f8f8f8") +``` + + +### Using with flextable + +By design, flextable does not support inserting raw HTML content into its cells. Using the `tbl_qmd_span()` or `tbl_qmd_div()` functions directly in a flextable will not work as expected. + +Quarto team will be working with flextable developers to find a way to support this in the future. + +### Using with **gt** + +The **gt** package provides a way to create tables with rich formatting. **gt** already has built-in support for rendering Markdown content, so you can use it directly without needing the `tbl_qmd_span()` or `tbl_qmd_div()` functions. + +Here is the same example as above, but using **gt** with **quarto** R package functions + +```{r} +library(gt) +gt(complex_table) |> + fmt_passthrough(columns = "Example", escape = FALSE) +``` + +Here is the example with built-in support for Markdown content in **gt**: + +```{r} +data.frame( + Feature = c("Formatting", "Math", "References", "Media"), + Example = c( + c("**Bold**, *italic*, and `code`"), + "$\\int_{a}^{b} f(x) \\, dx$", + "See @tbl-kable-equation for example of a table", + "{{< video https://www.youtube.com/embed/wo9vZccmqwc >}}" + ), + Notes = c( + "Basic markdown formatting", + "LaTeX math expressions", + "Cross-references to other document elements", + "Embedded media using shortcodes" + ) +) |> + gt() |> + fmt_markdown(columns = "Example") +``` + +`gt::fmt_markdown()` is aware of Quarto context and it will internally use the `data-qmd` attribute to render Markdown content correctly when Quarto processes the document. + +## Conclusion + +The table helper functions in this package make it easy to include Markdown content in HTML tables when working with Quarto documents. They will be useful to users to get unblocked when using package that provide HTML tables. Hopefully, developers will also find them useful to simplify the process for users of creating tables with rich content. + +For more information about tables in Quarto, see the [Quarto documentation on tables](https://quarto.org/docs/authoring/tables.html#html-tables). \ No newline at end of file From e736cc035ae4c6702cca956158cde2e53a15f7fe Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Wed, 25 Jun 2025 11:51:48 +0200 Subject: [PATCH 05/12] Hide warning from gt Issue is this one: https://github.com/rstudio/gt/issues/2004 --- vignettes/markdown-html-tables.qmd | 1 + 1 file changed, 1 insertion(+) diff --git a/vignettes/markdown-html-tables.qmd b/vignettes/markdown-html-tables.qmd index 34d02ad6..8130d03f 100644 --- a/vignettes/markdown-html-tables.qmd +++ b/vignettes/markdown-html-tables.qmd @@ -190,6 +190,7 @@ gt(complex_table) |> Here is the example with built-in support for Markdown content in **gt**: ```{r} +#| warning: false data.frame( Feature = c("Formatting", "Math", "References", "Media"), Example = c( From e096c57ecd9d330ab7768554ff44d6b0597777ee Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Wed, 25 Jun 2025 12:32:22 +0200 Subject: [PATCH 06/12] Add tinytable examples, and improve package documentation section --- vignettes/markdown-html-tables.qmd | 86 +++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 7 deletions(-) diff --git a/vignettes/markdown-html-tables.qmd b/vignettes/markdown-html-tables.qmd index 8130d03f..82e35583 100644 --- a/vignettes/markdown-html-tables.qmd +++ b/vignettes/markdown-html-tables.qmd @@ -133,9 +133,26 @@ Using `data-qmd` or `data-qmd-base64` attributes is a Quarto-specific feature an ## Table package integration +To summarize the question of Markdown processing in HTML tables within Quarto, + +- This is possible thanks to Quarto HTML Table parsing +- This is done using some `` or `
` elements with `data-qmd` or `data-qmd-base64` attributes + +Any R package for producing tables and providing raw HTML as output needs to support this Quarto feature to allow Markdown content in HTML tables. + +Currently, there is two ways this could be supported: + +- Either the package already supports Quarto HTML table parsing, and offer a way to mark the cells as to be processed by Quarto when in Quarto context. In this case, they will create themself the `` or `
` elements with the `data-qmd` or `data-qmd-base64` attributes. + +- Either the package does not support Quarto HTML table parsing directly, and offers a way to insert raw HTML content in the table cells. In this case, you can use the helper functions provided by this package to create the `` or `
` elements with the appropriate attributes. + +Below will show how this works with some popular R packages for creating tables. + ### Using with kableExtra -Here's a more complex example that combines all these features to create a complete HTML table with Markdown content: +**kableExtra** is a popular package for creating and styling tables in R. It produces raw HTML but does not have yet specific support for Quarto's HTML table parsing. However, you can use the helper functions to insert Markdown content into the cells, as it allows to insert raw HTML content in the table cells (by setting `escape = FALSE` to keep the raw HTML as-is). + +So here is a more complex example that combines all these features to create a complete HTML table with Markdown content: ```{r} library(quarto) @@ -168,18 +185,21 @@ kbl(complex_table, format = "html", escape = FALSE) %>% row_spec(0, bold = TRUE, background = "#f8f8f8") ``` +### Using with **flextable** -### Using with flextable +By design, **flextable** does not support inserting raw HTML content into its cells. Using the `tbl_qmd_span()` or `tbl_qmd_div()` functions directly in a flextable will not work as expected. -By design, flextable does not support inserting raw HTML content into its cells. Using the `tbl_qmd_span()` or `tbl_qmd_div()` functions directly in a flextable will not work as expected. +Unfortunately, **flextable** does not yet integrate with Quarto's HTML table parsing features, and does not allow to mark cell content as Markdown to be processed by Quarto. -Quarto team will be working with flextable developers to find a way to support this in the future. +The Quarto team will be working with **flextable** developers to find a way to support this in the future. ### Using with **gt** -The **gt** package provides a way to create tables with rich formatting. **gt** already has built-in support for rendering Markdown content, so you can use it directly without needing the `tbl_qmd_span()` or `tbl_qmd_div()` functions. +The **gt** package provides a way to create tables with rich formatting. + +**gt** allows to insert raw HTML content in the table cells, and it has built-in support for Quarto's HTML table parsing. It uses the `data-qmd` attribute internally to mark cells that contain Markdown content. -Here is the same example as above, but using **gt** with **quarto** R package functions +Here is the same table example as above, using **gt** with **quarto** R package functions. `fmt_passthrough()` is used to allow raw HTML content in the table cells, and `escape = FALSE` is set to avoid escaping the HTML content: ```{r} library(gt) @@ -187,6 +207,7 @@ gt(complex_table) |> fmt_passthrough(columns = "Example", escape = FALSE) ``` +However, **gt** already has built-in support for rendering Markdown content, so you can use it directly without needing the `tbl_qmd_span()` or `tbl_qmd_div()` functions. Here is the example with built-in support for Markdown content in **gt**: ```{r} @@ -212,8 +233,59 @@ data.frame( `gt::fmt_markdown()` is aware of Quarto context and it will internally use the `data-qmd` attribute to render Markdown content correctly when Quarto processes the document. +### Using with **tinytable** + +From the **tinytable** package website (): +> `tinytable` is a small but powerful R package to draw beautiful tables in a variety of formats: HTML, LaTeX, Word1, PDF, PNG, Markdown, and Typst. + +By default, `tinytable` does deactivate Quarto HTML table processing. It is a design choice so that **tinytable** formatting is not affected by Quarto's HTML table processing. So, our previous table would look like this: + +```{r} +library(tinytable) + +tt(complex_table) +``` + +Note that display value for video shortcode is used, as the shortcode is not processed by Quarto in this case. + +Quarto HTML table processing can be re-enabled in **tinytable**, and in that case, they will handle the `data-qmd` attribute internally, and functions `tbl_qmd_span()` and `tbl_qmd_div()` will not be needed. + +```{r} +options(tinytable_quarto_disable_processing = FALSE) +tt(complex_table) +``` + +Setting the option will opt-in the Quarto HTML table processing for all tables created with **tinytable**. This allows a table using `tbl_qmd_span()` or `tbl_qmd_div()` to be processed correctly by Quarto. +Let's unset option + +```{r} +options(tinytable_quarto_disable_processing = NULL) +``` + +Note that **tinytable** does support `data-qmd` attributes internally, so functions `tbl_qmd_span()` and `tbl_qmd_div()` are not needed when using **tinytable**. You can use `tt()` function directly with Markdown content in the table cells, and marking the cells as using Quarto Markdown processing. + +```{r} +data.frame( + Feature = c("Formatting", "Math", "References", "Media"), + Example = c( + c("**Bold**, *italic*, and `code`"), + "$\\int_{a}^{b} f(x) \\, dx$", + "See @tbl-kable-equation for example of a table", + "{{< video https://www.youtube.com/embed/wo9vZccmqwc >}}" + ), + Notes = c( + "Basic markdown formatting", + "LaTeX math expressions", + "Cross-references to other document elements", + "Embedded media using shortcodes" + ) +) |> + tt() |> + format_tt(j = "Example", quarto = TRUE) +``` + ## Conclusion -The table helper functions in this package make it easy to include Markdown content in HTML tables when working with Quarto documents. They will be useful to users to get unblocked when using package that provide HTML tables. Hopefully, developers will also find them useful to simplify the process for users of creating tables with rich content. +The table helper functions in this package make it easy to include Markdown content in HTML tables when working with Quarto documents. They will be useful to users to get unblocked when using a package that provide HTML tables, and does already support Quarto processing. Hopefully, developers will also find them useful to simplify the process for users of creating tables with rich content. This is already happening with **gt** and **tinytable** packages, which have built-in support for Markdown content in tables by marking the cells with the `data-qmd` attribute, internally for the user. For more information about tables in Quarto, see the [Quarto documentation on tables](https://quarto.org/docs/authoring/tables.html#html-tables). \ No newline at end of file From 439b71bbd0230cec20e36246a6b144bf87ec90ec Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Wed, 25 Jun 2025 13:03:52 +0200 Subject: [PATCH 07/12] Some review and improvement --- vignettes/markdown-html-tables.qmd | 100 ++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 30 deletions(-) diff --git a/vignettes/markdown-html-tables.qmd b/vignettes/markdown-html-tables.qmd index 82e35583..fccc787c 100644 --- a/vignettes/markdown-html-tables.qmd +++ b/vignettes/markdown-html-tables.qmd @@ -19,50 +19,90 @@ The main challenge when working with Markdown in HTML tables is that Quarto won' See Quarto documentation about HTML tables parsing: . +## When to Use These Functions + +Use the `tbl_qmd_*()` functions when: + +- **Working with table packages that don't have built-in Quarto support** (like kableExtra) +- **You need raw HTML control** over some Markdown content that needs to be processed by Quarto +- **Migrating existing table code** to support Quarto's HTML table processing + +**Don't use these functions** when: + +- Your table package already has built-in Quarto support (like gt's `fmt_markdown()` or tinytable's `format_tt(quarto = TRUE)`) +- You're working outside of Quarto documents (the functions will have no effect) +- Simple formatting is easy enough to write in raw HTML and does not require Quarto Markdown processing. + ## Basic Usage -The table helper functions create HTML elements (`` or `
`) with the appropriate `data-qmd` or `qmd-base64` attributes. There are two main types of functions: +The table helper functions create HTML elements (`` or `
`) with the appropriate `data-qmd` or `data-qmd-base64` attributes. There are two main types of functions: 1. Functions for creating `` elements: - - main function is `tbl_qmd_span()`, defaulting to base64 encoding, - - Two others are explicit versions: `tbl_qmd_span_base64()` and `tbl_qmd_span_raw()` + - main function is `tbl_qmd_span()`, defaulting to base64 encoding + - Two others are explicit versions: `tbl_qmd_span_base64()` and `tbl_qmd_span_raw()` 2. Functions for creating `
` elements: - - main function is `tbl_qmd_div()`, defaulting to base64 encoding, - - Two others are explicit versions: `tbl_qmd_div_base64()` and `tbl_qmd_div_raw()` + - main function is `tbl_qmd_div()`, defaulting to base64 encoding + - Two others are explicit versions: `tbl_qmd_div_base64()` and `tbl_qmd_div_raw()` Base64 encoding is useful when your Markdown content contains special characters or HTML tags, and this is used by default to avoid any escaping problems using this feature. -### Example with a Basic HTML Table +### Before and After Comparison -Here's a simple example of creating an HTML table with Markdown content: +Here's what happens when you don't use the helper functions: ```{r} library(quarto) -# Create a simple data frame -data <- data.frame( - Column1 = c("Row 1", "Row 2", "Row 3"), - Column2 = c("Value 1", "Value 2", "Value 3") +# Without helper functions - Markdown won't be processed +basic_data <- data.frame( + Item = c("Item 1", "Item 2", "Item 3"), + Description = c("**Bold text**", "*Italic text*", "`Code text`") +) + +knitr::kable( + basic_data, + format = "html", + escape = FALSE, + caption = "Without Quarto processing" +) +``` + +And here's the same table with proper Quarto processing: + +```{r} +# With helper functions - Markdown will be processed +enhanced_data <- data.frame( + Item = c("Item 1", "Item 2", "Item 3"), + Description = c( + tbl_qmd_span("**Bold text**"), + tbl_qmd_span("*Italic text*"), + tbl_qmd_span("`Code text`") + ) ) -# Function to add Markdown formatting to table cells -add_markdown <- function(data) { - data$Column1 <- sapply(data$Column1, function(x) { - tbl_qmd_span(paste0("**", x, "**")) - }) - data$Column2 <- sapply(data$Column2, function(x) { - tbl_qmd_span(paste0("*", x, "*")) - }) - return(data) -} - -# Apply Markdown formatting -data_with_md <- add_markdown(data) - -# Display the data frame as an HTML table -knitr::kable(data_with_md, format = "html", escape = FALSE) +knitr::kable( + enhanced_data, + format = "html", + escape = FALSE, + caption = "With Quarto processing" +) ``` +**Key point**: Always remember to set `escape = FALSE` when using these functions with `knitr::kable()` or similar functions. + +### What the HTML Output Looks Like + +When you use `tbl_qmd_span("**Bold text**")`, it creates HTML like this: + +```{r} +#| echo: false +#| output: asis +xfun::fenced_block(attrs = ".html", tbl_qmd_span("**Bold text**")) |> + cat(sep = "\n") +``` + +Quarto sees the `data-qmd-base64` attribute and processes the base64-decoded content as Markdown. + ### Using with knitr::kable() The `knitr::kable()` function is a common way to create tables in R Markdown and Quarto. By setting `escape = FALSE`, we can include HTML in the table cells: @@ -89,7 +129,7 @@ knitr::kable(tbl, format = "html", escape = FALSE) ### Display Text -Some feature are Quarto features only. If your table can be used in other context than Quarto, you might want to use the `display` argument to provide a text that will be shown in the table instead of the Markdown content, as it won't be processed outside of Quarto documents. +Some features are Quarto features only. If your table can be used in other context than Quarto, you might want to use the `display` argument to provide a text that will be shown in the table instead of the Markdown content, as it won't be processed outside of Quarto documents. For example, you might want to show a placeholder when using video shortcodes in a table, as the video player won't be rendered outside of Quarto: @@ -133,12 +173,12 @@ Using `data-qmd` or `data-qmd-base64` attributes is a Quarto-specific feature an ## Table package integration -To summarize the question of Markdown processing in HTML tables within Quarto, +To summarize the question of Markdown processing in HTML tables within Quarto, currently, there are two ways this could be supported: - This is possible thanks to Quarto HTML Table parsing - This is done using some `` or `
` elements with `data-qmd` or `data-qmd-base64` attributes -Any R package for producing tables and providing raw HTML as output needs to support this Quarto feature to allow Markdown content in HTML tables. +Any R package for producing tables and providing raw HTML as output needs to support this Quarto feature to allow Markdown content in HTML tables.In this case, they will create themselves the `` or `
` elements with the `data-qmd` or `data-qmd-base64` attributes. Currently, there is two ways this could be supported: From 7be6034bcc829cbc8d4e61239125cd8e54609059 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Wed, 25 Jun 2025 13:13:19 +0200 Subject: [PATCH 08/12] New review --- vignettes/markdown-html-tables.qmd | 142 ++++++++++++++++++++++------- 1 file changed, 110 insertions(+), 32 deletions(-) diff --git a/vignettes/markdown-html-tables.qmd b/vignettes/markdown-html-tables.qmd index fccc787c..f851d037 100644 --- a/vignettes/markdown-html-tables.qmd +++ b/vignettes/markdown-html-tables.qmd @@ -31,7 +31,7 @@ Use the `tbl_qmd_*()` functions when: - Your table package already has built-in Quarto support (like gt's `fmt_markdown()` or tinytable's `format_tt(quarto = TRUE)`) - You're working outside of Quarto documents (the functions will have no effect) -- Simple formatting is easy enough to write in raw HTML and does not require Quarto Markdown processing. +- Simple formatting is easy enough to write in raw HTML and does not require Quarto Markdown processing ## Basic Usage @@ -103,6 +103,27 @@ xfun::fenced_block(attrs = ".html", tbl_qmd_span("**Bold text**")) |> Quarto sees the `data-qmd-base64` attribute and processes the base64-decoded content as Markdown. +### Base64 vs Raw Encoding + +The helper functions offer two encoding options: + +- **Base64 encoding** (default): Safer for complex content with special characters +- **Raw encoding**: More readable in HTML source, but can have escaping issues + +```{r} +# Base64 encoding (default) - safer for complex content +complex_content <- tbl_qmd_span_base64("Content with HTML & special chars") + +# Raw encoding - more readable but potential escaping issues +simple_content <- tbl_qmd_span_raw("**Simple bold text**") + +data.frame( + Type = c("Base64", "Raw"), + Content = c(complex_content, simple_content) +) |> + knitr::kable(format = "html", escape = FALSE) +``` + ### Using with knitr::kable() The `knitr::kable()` function is a common way to create tables in R Markdown and Quarto. By setting `escape = FALSE`, we can include HTML in the table cells: @@ -110,7 +131,6 @@ The `knitr::kable()` function is a common way to create tables in R Markdown and ```{r} #| label: tbl-kable-equation #| tbl-cap: A table with a math equation rendered using Quarto's data-qmd attribute -library(quarto) # Create a data frame with math expressions tbl <- data.frame( @@ -129,7 +149,7 @@ knitr::kable(tbl, format = "html", escape = FALSE) ### Display Text -Some features are Quarto features only. If your table can be used in other context than Quarto, you might want to use the `display` argument to provide a text that will be shown in the table instead of the Markdown content, as it won't be processed outside of Quarto documents. +Some features are Quarto-specific. If your table might be used outside of Quarto, you can use the `display` argument to provide fallback text that will be shown when the Markdown content can't be processed. For example, you might want to show a placeholder when using video shortcodes in a table, as the video player won't be rendered outside of Quarto: @@ -151,7 +171,7 @@ data <- data.frame( knitr::kable(data, format = "html", escape = FALSE) ``` -Behavior when the table is not processed by Quarto is simulated by opting-out html table processing for this specific table. For example, when `html-table-processing: none` cell option is set like in the Quarto computation cell below. +Behavior when the table is not processed by Quarto is simulated by opting-out HTML table processing for this specific table. For example, when `html-table-processing: none` cell option is set like in the Quarto computation cell below. ```{r} #| label: video-placeholder @@ -159,43 +179,101 @@ Behavior when the table is not processed by Quarto is simulated by opting-out ht #| html-table-processing: none ``` -Output above is a HTML table not processed by Quarto, so the video shortcode is not rendered as a video player, but as a regular text. +Output above is an HTML table not processed by Quarto, so the video shortcode is not rendered as a video player, but as regular text. See more about disabling HTML table processing in the [Quarto documentation](https://quarto.org/docs/authoring/tables.html#disabling-quarto-table-processing). +## Troubleshooting + +### Common Issues + +**Content not rendering as Markdown:** +- Check that `escape = FALSE` is set in your table function +- Verify that Quarto HTML table processing is enabled (it's on by default) +- Ensure you're using the functions in a Quarto document + +**Escaping problems:** +- Use base64 encoding (the default) for content with special characters +- Use `tbl_qmd_span_base64()` explicitly for complex HTML content + +**Performance with large tables:** +- Base64 encoding adds some overhead - consider raw encoding for simple content in very large tables +- Test with your specific use case to determine if performance is acceptable + +**Content appears as HTML tags:** +- You may have forgotten `escape = FALSE` in your table function +- Double-check that your table package supports raw HTML content + +### Testing Your Setup + +Use this simple test to verify everything is working: + +```{r} +test_data <- data.frame( + Test = "Markdown Processing", + Result = tbl_qmd_span("**This should be bold**") +) + +knitr::kable(test_data, format = "html", escape = FALSE) +``` + +If the text appears bold, your setup is working correctly. + ::: {.callout-important} ## Limitations -Using `data-qmd` or `data-qmd-base64` attributes is a Quarto-specific feature and it will only be working when Quarto is allowed to process HTML tables. If this is used in an environment or a document that do opt-out Quarto HTML table processing, the content will not be rendered as expected. +Using `data-qmd` or `data-qmd-base64` attributes is a Quarto-specific feature and will only work when Quarto is allowed to process HTML tables. If this is used in an environment or document that opts out of Quarto HTML table processing, the content will not be rendered as expected. ::: -## Table package integration +## Table Package Integration -To summarize the question of Markdown processing in HTML tables within Quarto, currently, there are two ways this could be supported: +To summarize Markdown processing in HTML tables within Quarto: - This is possible thanks to Quarto HTML Table parsing -- This is done using some `` or `
` elements with `data-qmd` or `data-qmd-base64` attributes +- This is done using `` or `
` elements with `data-qmd` or `data-qmd-base64` attributes + +Any R package for producing tables and providing raw HTML as output can support this Quarto feature to allow Markdown content in HTML tables. + +Currently, there are two ways this can be supported: + +- **Built-in support**: The package already supports Quarto HTML table parsing and offers a way to mark cells as to be processed by Quarto when in Quarto context. In this case, they create the `` or `
` elements with the `data-qmd` or `data-qmd-base64` attributes internally. -Any R package for producing tables and providing raw HTML as output needs to support this Quarto feature to allow Markdown content in HTML tables.In this case, they will create themselves the `` or `
` elements with the `data-qmd` or `data-qmd-base64` attributes. +- **External helper approach**: The package does not support Quarto HTML table parsing directly but offers a way to insert raw HTML content in table cells. In this case, you can use the helper functions provided by this package to create the `` or `
` elements with the appropriate attributes. -Currently, there is two ways this could be supported: +Below we show how this works with some popular R packages for creating tables. -- Either the package already supports Quarto HTML table parsing, and offer a way to mark the cells as to be processed by Quarto when in Quarto context. In this case, they will create themself the `` or `
` elements with the `data-qmd` or `data-qmd-base64` attributes. +### For Package Developers -- Either the package does not support Quarto HTML table parsing directly, and offers a way to insert raw HTML content in the table cells. In this case, you can use the helper functions provided by this package to create the `` or `
` elements with the appropriate attributes. +If you're developing an R package that creates HTML tables, consider: -Below will show how this works with some popular R packages for creating tables. +**Integration Options:** +1. **Full integration**: Add native support for `data-qmd` attributes (like gt and tinytable) +2. **Raw HTML support**: Allow users to insert raw HTML and let them use these helper functions +3. **Hybrid approach**: Detect Quarto context and automatically apply appropriate attributes + +**Recommended Implementation:** +```r +# Example function signature for package developers +your_table_function <- function(data, markdown_cols = NULL, quarto = TRUE) { + # If quarto = TRUE and in Quarto context, apply data-qmd attributes + # to columns specified in markdown_cols +} +``` + +**Testing Strategy:** +- Test both inside and outside Quarto documents +- Verify that fallback `display` text works correctly +- Test with various Markdown content types (math, links, formatting) ### Using with kableExtra -**kableExtra** is a popular package for creating and styling tables in R. It produces raw HTML but does not have yet specific support for Quarto's HTML table parsing. However, you can use the helper functions to insert Markdown content into the cells, as it allows to insert raw HTML content in the table cells (by setting `escape = FALSE` to keep the raw HTML as-is). +**kableExtra** is a popular package for creating and styling tables in R. It produces raw HTML but does not have specific support for Quarto's HTML table parsing. However, you can use the helper functions to insert Markdown content into the cells, as it allows inserting raw HTML content in table cells (by setting `escape = FALSE` to keep the raw HTML as-is). -So here is a more complex example that combines all these features to create a complete HTML table with Markdown content: +Here is a more complex example that combines all these features to create a complete HTML table with Markdown content: ```{r} -library(quarto) library(kableExtra) # Create a data frame with different types of content @@ -219,9 +297,9 @@ complex_table <- data.frame( ) # Create and style the table -kbl(complex_table, format = "html", escape = FALSE) %>% - kable_classic() %>% - column_spec(2, width = "40%") %>% +kbl(complex_table, format = "html", escape = FALSE) |> + kable_classic() |> + column_spec(2, width = "40%") |> row_spec(0, bold = TRUE, background = "#f8f8f8") ``` @@ -229,7 +307,7 @@ kbl(complex_table, format = "html", escape = FALSE) %>% By design, **flextable** does not support inserting raw HTML content into its cells. Using the `tbl_qmd_span()` or `tbl_qmd_div()` functions directly in a flextable will not work as expected. -Unfortunately, **flextable** does not yet integrate with Quarto's HTML table parsing features, and does not allow to mark cell content as Markdown to be processed by Quarto. +Unfortunately, **flextable** does not yet integrate with Quarto's HTML table parsing features, and does not allow marking cell content as Markdown to be processed by Quarto. The Quarto team will be working with **flextable** developers to find a way to support this in the future. @@ -237,7 +315,7 @@ The Quarto team will be working with **flextable** developers to find a way to s The **gt** package provides a way to create tables with rich formatting. -**gt** allows to insert raw HTML content in the table cells, and it has built-in support for Quarto's HTML table parsing. It uses the `data-qmd` attribute internally to mark cells that contain Markdown content. +**gt** allows inserting raw HTML content in table cells and has built-in support for Quarto's HTML table parsing. It uses the `data-qmd` attribute internally to mark cells that contain Markdown content. Here is the same table example as above, using **gt** with **quarto** R package functions. `fmt_passthrough()` is used to allow raw HTML content in the table cells, and `escape = FALSE` is set to avoid escaping the HTML content: @@ -255,7 +333,7 @@ Here is the example with built-in support for Markdown content in **gt**: data.frame( Feature = c("Formatting", "Math", "References", "Media"), Example = c( - c("**Bold**, *italic*, and `code`"), + "**Bold**, *italic*, and `code`", "$\\int_{a}^{b} f(x) \\, dx$", "See @tbl-kable-equation for example of a table", "{{< video https://www.youtube.com/embed/wo9vZccmqwc >}}" @@ -271,14 +349,14 @@ data.frame( fmt_markdown(columns = "Example") ``` -`gt::fmt_markdown()` is aware of Quarto context and it will internally use the `data-qmd` attribute to render Markdown content correctly when Quarto processes the document. +`gt::fmt_markdown()` is aware of Quarto context and will internally use the `data-qmd` attribute to render Markdown content correctly when Quarto processes the document. ### Using with **tinytable** From the **tinytable** package website (): -> `tinytable` is a small but powerful R package to draw beautiful tables in a variety of formats: HTML, LaTeX, Word1, PDF, PNG, Markdown, and Typst. +> `tinytable` is a small but powerful R package to draw beautiful tables in a variety of formats: HTML, LaTeX, Word, PDF, PNG, Markdown, and Typst. -By default, `tinytable` does deactivate Quarto HTML table processing. It is a design choice so that **tinytable** formatting is not affected by Quarto's HTML table processing. So, our previous table would look like this: +By default, `tinytable` deactivates Quarto HTML table processing. This is a design choice so that **tinytable** formatting is not affected by Quarto's HTML table processing. So, our previous table would look like this: ```{r} library(tinytable) @@ -286,7 +364,7 @@ library(tinytable) tt(complex_table) ``` -Note that display value for video shortcode is used, as the shortcode is not processed by Quarto in this case. +Note that the display value for the video shortcode is used, as the shortcode is not processed by Quarto in this case. Quarto HTML table processing can be re-enabled in **tinytable**, and in that case, they will handle the `data-qmd` attribute internally, and functions `tbl_qmd_span()` and `tbl_qmd_div()` will not be needed. @@ -295,20 +373,20 @@ options(tinytable_quarto_disable_processing = FALSE) tt(complex_table) ``` -Setting the option will opt-in the Quarto HTML table processing for all tables created with **tinytable**. This allows a table using `tbl_qmd_span()` or `tbl_qmd_div()` to be processed correctly by Quarto. -Let's unset option +Setting the option will opt-in to Quarto HTML table processing for all tables created with **tinytable**. This allows a table using `tbl_qmd_span()` or `tbl_qmd_div()` to be processed correctly by Quarto. +Let's unset the option: ```{r} options(tinytable_quarto_disable_processing = NULL) ``` -Note that **tinytable** does support `data-qmd` attributes internally, so functions `tbl_qmd_span()` and `tbl_qmd_div()` are not needed when using **tinytable**. You can use `tt()` function directly with Markdown content in the table cells, and marking the cells as using Quarto Markdown processing. +Note that **tinytable** supports `data-qmd` attributes internally, so functions `tbl_qmd_span()` and `tbl_qmd_div()` are not needed when using **tinytable**. You can use `tt()` function directly with Markdown content in the table cells, and mark the cells as using Quarto Markdown processing. ```{r} data.frame( Feature = c("Formatting", "Math", "References", "Media"), Example = c( - c("**Bold**, *italic*, and `code`"), + "**Bold**, *italic*, and `code`", "$\\int_{a}^{b} f(x) \\, dx$", "See @tbl-kable-equation for example of a table", "{{< video https://www.youtube.com/embed/wo9vZccmqwc >}}" @@ -326,6 +404,6 @@ data.frame( ## Conclusion -The table helper functions in this package make it easy to include Markdown content in HTML tables when working with Quarto documents. They will be useful to users to get unblocked when using a package that provide HTML tables, and does already support Quarto processing. Hopefully, developers will also find them useful to simplify the process for users of creating tables with rich content. This is already happening with **gt** and **tinytable** packages, which have built-in support for Markdown content in tables by marking the cells with the `data-qmd` attribute, internally for the user. +The table helper functions in this package make it easy to include Markdown content in HTML tables when working with Quarto documents. They are useful for users to get unblocked when using a package that provides HTML tables but doesn't already support Quarto processing. Hopefully, developers will also find them useful to simplify the process for users of creating tables with rich content. This is already happening with **gt** and **tinytable** packages, which have built-in support for Markdown content in tables by marking the cells with the `data-qmd` attribute internally for the user. For more information about tables in Quarto, see the [Quarto documentation on tables](https://quarto.org/docs/authoring/tables.html#html-tables). \ No newline at end of file From 933ff99911295a9757643e387ec5cbe70c7649fd Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Wed, 25 Jun 2025 13:16:56 +0200 Subject: [PATCH 09/12] don't expose class and attr yet --- R/table-helper.R | 58 ++++++++++-------------------- man/tbl_qmd_elements.Rd | 35 ++++-------------- tests/testthat/test-table-helper.R | 42 ++++++++-------------- 3 files changed, 39 insertions(+), 96 deletions(-) diff --git a/R/table-helper.R b/R/table-helper.R index 4376dfc4..301f4489 100644 --- a/R/table-helper.R +++ b/R/table-helper.R @@ -27,10 +27,6 @@ #' accessibility. #' @param use_base64 Logical, whether to base64 encode the content (recommended #' for complex content with special characters or when content includes quotes) -#' @param class Optional CSS class(es) to add to the element. While this works for -#' both span and div elements, it's more commonly used with div elements. -#' @param attrs Named list of additional HTML attributes to add to the element. -#' For example: `list(id = "my-element", title = "Tooltip text")` #' #' @return Character string containing the HTML element with appropriate data-qmd attributes #' @@ -47,11 +43,6 @@ #' tbl_qmd_span_base64("Complex $\\LaTeX$ content") #' tbl_qmd_span_raw("Simple text") #' -#' # Use with custom attributes -#' tbl_qmd_span("**Important note**", attrs = list(title = "Hover for more info")) -#' tbl_qmd_div("Content here", class = "callout", -#' attrs = list(id = "special-note", tabindex = "0")) -#' #' # Use with different HTML table packages #' \dontrun{ #' # With kableExtra @@ -85,7 +76,12 @@ NULL invisible(TRUE) } - +#' @inheritParams tbl_qmd_elements +#' @param class Optional CSS class(es) to add to the element. While this works for +#' both span and div elements, it's more commonly used with div elements. +#' @param attrs Named list of additional HTML attributes to add to the element. +#' For example: `list(id = "my-element", title = "Tooltip text")` +#' @noRd .tbl_qmd_element <- function( tag, content, @@ -132,11 +128,9 @@ NULL tbl_qmd_span <- function( content, display = NULL, - use_base64 = TRUE, - class = NULL, - attrs = NULL + use_base64 = TRUE ) { - .tbl_qmd_element("span", content, display, use_base64, class, attrs) + .tbl_qmd_element("span", content, display, use_base64) } #' @rdname tbl_qmd_elements @@ -144,26 +138,20 @@ tbl_qmd_span <- function( tbl_qmd_div <- function( content, display = NULL, - use_base64 = TRUE, - class = NULL, - attrs = NULL + use_base64 = TRUE ) { - .tbl_qmd_element("div", content, display, use_base64, class, attrs) + .tbl_qmd_element("div", content, display, use_base64) } #' @rdname tbl_qmd_elements #' @export tbl_qmd_span_base64 <- function( content, - display = NULL, - class = NULL, - attrs = NULL + display = NULL ) { tbl_qmd_span( content, display, - use_base64 = TRUE, - class = class, - attrs = attrs + use_base64 = TRUE ) } @@ -171,27 +159,21 @@ tbl_qmd_span_base64 <- function( #' @export tbl_qmd_div_base64 <- function( content, - display = NULL, - class = NULL, - attrs = NULL + display = NULL ) { - tbl_qmd_div(content, display, use_base64 = TRUE, class = class, attrs = attrs) + tbl_qmd_div(content, display, use_base64 = TRUE) } #' @rdname tbl_qmd_elements #' @export tbl_qmd_span_raw <- function( content, - display = NULL, - class = NULL, - attrs = NULL + display = NULL ) { tbl_qmd_span( content, display, - use_base64 = FALSE, - class = class, - attrs = attrs + use_base64 = FALSE ) } @@ -199,15 +181,11 @@ tbl_qmd_span_raw <- function( #' @export tbl_qmd_div_raw <- function( content, - display = NULL, - class = NULL, - attrs = NULL + display = NULL ) { tbl_qmd_div( content, display, - use_base64 = FALSE, - class = class, - attrs = attrs + use_base64 = FALSE ) } diff --git a/man/tbl_qmd_elements.Rd b/man/tbl_qmd_elements.Rd index b5b97f2d..c0ef9fab 100644 --- a/man/tbl_qmd_elements.Rd +++ b/man/tbl_qmd_elements.Rd @@ -10,29 +10,17 @@ \alias{tbl_qmd_div_raw} \title{Create Quarto Markdown HTML Elements for Tables} \usage{ -tbl_qmd_span( - content, - display = NULL, - use_base64 = TRUE, - class = NULL, - attrs = NULL -) +tbl_qmd_span(content, display = NULL, use_base64 = TRUE) -tbl_qmd_div( - content, - display = NULL, - use_base64 = TRUE, - class = NULL, - attrs = NULL -) +tbl_qmd_div(content, display = NULL, use_base64 = TRUE) -tbl_qmd_span_base64(content, display = NULL, class = NULL, attrs = NULL) +tbl_qmd_span_base64(content, display = NULL) -tbl_qmd_div_base64(content, display = NULL, class = NULL, attrs = NULL) +tbl_qmd_div_base64(content, display = NULL) -tbl_qmd_span_raw(content, display = NULL, class = NULL, attrs = NULL) +tbl_qmd_span_raw(content, display = NULL) -tbl_qmd_div_raw(content, display = NULL, class = NULL, attrs = NULL) +tbl_qmd_div_raw(content, display = NULL) } \arguments{ \item{content}{Character string of content to wrap. This can include Markdown, @@ -44,12 +32,6 @@ accessibility.} \item{use_base64}{Logical, whether to base64 encode the content (recommended for complex content with special characters or when content includes quotes)} - -\item{class}{Optional CSS class(es) to add to the element. While this works for -both span and div elements, it's more commonly used with div elements.} - -\item{attrs}{Named list of additional HTML attributes to add to the element. -For example: \code{list(id = "my-element", title = "Tooltip text")}} } \value{ Character string containing the HTML element with appropriate data-qmd attributes @@ -89,11 +71,6 @@ tbl_qmd_div("{{< video https://example.com >}}", display = "[Video content]") tbl_qmd_span_base64("Complex $\\\\LaTeX$ content") tbl_qmd_span_raw("Simple text") -# Use with custom attributes -tbl_qmd_span("**Important note**", attrs = list(title = "Hover for more info")) -tbl_qmd_div("Content here", class = "callout", - attrs = list(id = "special-note", tabindex = "0")) - # Use with different HTML table packages \dontrun{ # With kableExtra diff --git a/tests/testthat/test-table-helper.R b/tests/testthat/test-table-helper.R index 1c099596..b3a5ceea 100644 --- a/tests/testthat/test-table-helper.R +++ b/tests/testthat/test-table-helper.R @@ -9,19 +9,6 @@ test_that("tbl_qmd_span generates correct HTML with base64 encoding", { "Greek formula", fixed = TRUE ) - expect_match( - tbl_qmd_span("**bold text**", class = "highlight"), - "**bold text**", - fixed = TRUE - ) - expect_match( - tbl_qmd_span( - "**Important note**", - attrs = list(title = "Hover for more info") - ), - "**Important note**", - fixed = TRUE - ) }) test_that("tbl_qmd_span_raw generates correct HTML with raw encoding", { @@ -49,20 +36,6 @@ test_that("tbl_qmd_div generates correct HTML with base64 encoding", { "
[Video content]
", fixed = TRUE ) - - expect_match( - tbl_qmd_div("Content here", class = "something"), - "
Content here
", - fixed = TRUE - ) - expect_match( - tbl_qmd_div( - "Content here", - attrs = list(id = "special-note", tabindex = "0") - ), - "
Content here
", - fixed = TRUE - ) }) test_that("tbl_qmd_div_raw generates correct HTML with raw encoding", { @@ -109,3 +82,18 @@ test_that(".tbl_qmd_element correctly handles different tag types", { fixed = TRUE ) }) + +test_that(".tbl_qmd_element handles class and additional attributes", { + expect_match( + .tbl_qmd_element( + "span", + "content", + "display", + TRUE, + class = "test-class", + attrs = list(id = "test-id", tabindex = "0") + ), + "display", + fixed = TRUE + ) +}) From 8953ad2e1a0b6e0cbdde6d82fec763de251f0a6d Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Wed, 25 Jun 2025 13:19:49 +0200 Subject: [PATCH 10/12] Remove keep-md --- vignettes/markdown-html-tables.qmd | 1 - 1 file changed, 1 deletion(-) diff --git a/vignettes/markdown-html-tables.qmd b/vignettes/markdown-html-tables.qmd index f851d037..45b22775 100644 --- a/vignettes/markdown-html-tables.qmd +++ b/vignettes/markdown-html-tables.qmd @@ -4,7 +4,6 @@ format: html: toc: true toc-depth: 3 -keep-md: true vignette: > %\VignetteIndexEntry{Using Markdown in HTML Tables} %\VignetteEngine{quarto::html} From c85bdf2c0c46f9ce2ff2bdef826171d1644c1ded Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Wed, 25 Jun 2025 13:58:52 +0200 Subject: [PATCH 11/12] tinytable needs to be added in suggests --- DESCRIPTION | 1 + 1 file changed, 1 insertion(+) diff --git a/DESCRIPTION b/DESCRIPTION index fdc06263..0339cdb5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -51,6 +51,7 @@ Suggests: testthat (>= 3.1.7), thematic, tidyverse, + tinytable, whoami, withr VignetteBuilder: From 464c8f9cc8fa332f10cc1e1d568ad89ddac7d4dc Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Wed, 25 Jun 2025 14:02:17 +0200 Subject: [PATCH 12/12] Protect against missing package --- vignettes/markdown-html-tables.qmd | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/vignettes/markdown-html-tables.qmd b/vignettes/markdown-html-tables.qmd index 45b22775..35ce3f01 100644 --- a/vignettes/markdown-html-tables.qmd +++ b/vignettes/markdown-html-tables.qmd @@ -273,6 +273,7 @@ your_table_function <- function(data, markdown_cols = NULL, quarto = TRUE) { Here is a more complex example that combines all these features to create a complete HTML table with Markdown content: ```{r} +#| eval: !expr requireNamespace("kableExtra", quietly = TRUE) library(kableExtra) # Create a data frame with different types of content @@ -319,6 +320,7 @@ The **gt** package provides a way to create tables with rich formatting. Here is the same table example as above, using **gt** with **quarto** R package functions. `fmt_passthrough()` is used to allow raw HTML content in the table cells, and `escape = FALSE` is set to avoid escaping the HTML content: ```{r} +#| eval: !expr requireNamespace("gt", quietly = TRUE) library(gt) gt(complex_table) |> fmt_passthrough(columns = "Example", escape = FALSE) @@ -329,6 +331,7 @@ Here is the example with built-in support for Markdown content in **gt**: ```{r} #| warning: false +#| eval: !expr requireNamespace("gt", quietly = TRUE) data.frame( Feature = c("Formatting", "Math", "References", "Media"), Example = c( @@ -358,6 +361,7 @@ From the **tinytable** package website (