From 79f4f106b8193f27f10b21184a61a8c59d40d4c8 Mon Sep 17 00:00:00 2001 From: Justin Yap Date: Mon, 13 Jul 2026 15:21:36 +1000 Subject: [PATCH 1/2] RS-22593: Expose pictograph value as ChartData and title as ChartLabels$ChartTitle VisualizeNumber and PictographChart rendered their value (and, for VisualizeNumber, a title via text.above) but did not expose them on the output, so consumers reading attr(x, "ChartData") / attr(x, "ChartLabels") (e.g. the LLM-readable generation) had nothing to read or label. VisualizeNumber now attaches the input value as ChartData and text.above as ChartLabels$ChartTitle (when non-empty), applied to both of its return paths. PictographChart attaches its cleaned data matrix as ChartData; it has no title. Co-Authored-By: Claude Opus 4.8 (1M context) --- R/pictographchart.R | 8 +++++++- R/visualizenumber.R | 21 ++++++++++++++++++--- tests/testthat/test-pictographchart.R | 6 ++++++ tests/testthat/test-visualizenumber.R | 19 +++++++++++++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/R/pictographchart.R b/R/pictographchart.R index d04a6e3..5278846 100644 --- a/R/pictographchart.R +++ b/R/pictographchart.R @@ -320,6 +320,10 @@ PictographChart <- function(x, } x <- RemoveRowsAndOrColumns(x, row.names.to.remove, column.names.to.remove) + # Cleaned data matrix the pictograph represents, captured before x is + # rescaled for icon rendering, so it can be exposed as the chart's data. + chart.data <- x + # Data labels label.data.values <- unlist(x) * (1+(99*label.data.100prc)) if (!customize.label.data && max(label.data.values) <= 1) @@ -598,5 +602,7 @@ PictographChart <- function(x, if(is.numeric(json)) f.mspace <- f.mspace + json } - return(graphic(json)) + g <- graphic(json) + attr(g, "ChartData") <- chart.data + return(g) } diff --git a/R/visualizenumber.R b/R/visualizenumber.R index c0d4e5a..e38397b 100644 --- a/R/visualizenumber.R +++ b/R/visualizenumber.R @@ -183,6 +183,21 @@ VisualizeNumber <- function(x, margin.bottom = 0, ...) { + # Snapshot the input value before any normalisation (e.g. the percentage + # rescaling of x below) so it can be exposed unchanged as the chart's data. + chart.data <- x + + # Attaches the visualized value as ChartData and the title (text.above) as + # ChartLabels$ChartTitle, so consumers can read the value and label it. + # Applied to whichever widget this function returns. + addChartAttributes <- function(widget) + { + attr(widget, "ChartData") <- chart.data + if (any(nzchar(text.above))) + attr(widget, "ChartLabels") <- list(ChartTitle = text.above) + widget + } + display <- switch(tolower(display), oval = "circle", circle = "circle", "number in an oval" = "circle", rectangle = "rectangle", square = "rectangle", "number in a rectangle" = "rectangle", number = "number", @@ -299,7 +314,7 @@ VisualizeNumber <- function(x, } if (label.data.position == "None") label.str <- "" - return(iconsWithText(value, fill.icon.color = fill.color, + return(addChartAttributes(iconsWithText(value, fill.icon.color = fill.color, base.icon.color = base.color, maximum.value = maximum.value, total.icons = total.icons, ..., # other icon parameters? text.overlay = label.str, text.overlay.halign = tolower(label.data.halign), @@ -322,7 +337,7 @@ VisualizeNumber <- function(x, text.above.font.weight = tolower(text.above.font.weight), background.color = if (background.opacity > 0) background.color else "transparent", margin.top = margin.top, margin.right = margin.right, - margin.bottom = margin.bottom, margin.left = margin.left)) + margin.bottom = margin.bottom, margin.left = margin.left))) } if (display %in% c("donut", "gauge", "bar")) @@ -490,7 +505,7 @@ VisualizeNumber <- function(x, p$sizingPolicy$browser$padding <- 0 class(p) <- c(class(p), "visualization-selector") attr(p, "can-run-in-root-dom") <- TRUE - return(p) + return(addChartAttributes(p)) } setText <- function(text, yalign, xalign, font, font.weight, # parameters always supplied diff --git a/tests/testthat/test-pictographchart.R b/tests/testthat/test-pictographchart.R index 4435ef2..ddf3502 100644 --- a/tests/testthat/test-pictographchart.R +++ b/tests/testthat/test-pictographchart.R @@ -20,3 +20,9 @@ test_that("Wide pictograph", { # expect_error(suppressWarnings(PictographChart(x2, mode="bar", hide.base.image=T, graphic.width.inch=244/72, graphic.height.inch=583/72, # show.label.data=T, label.data.position="Next to bar")), "Window is too narrow.") }) + +test_that("ChartData exposed, no ChartTitle", { + res <- PictographChart(x1, show.legend = TRUE) + expect_equal(as.numeric(attr(res, "ChartData")), as.numeric(x1)) + expect_null(attr(res, "ChartLabels")) +}) diff --git a/tests/testthat/test-visualizenumber.R b/tests/testthat/test-visualizenumber.R index 3045919..b2181dd 100644 --- a/tests/testthat/test-visualizenumber.R +++ b/tests/testthat/test-visualizenumber.R @@ -76,3 +76,22 @@ test_that("VisualizeNumber with SelectEntry", expect_equal(attr(value, "format"), "%") expect_equal(value, 0.122449, check.attributes = FALSE, tol = 1e-3) }) + +test_that("ChartData and ChartTitle exposed on output", +{ + # Main (non-icon) return path + res <- VisualizeNumber(0.4, maximum.value = 1.0, text.above = "My title") + expect_equal(as.numeric(attr(res, "ChartData")), 0.4) + expect_equal(attr(res, "ChartLabels")$ChartTitle, "My title") + + # Icon/pictograph return path + res.icon <- VisualizeNumber(ParseText("40%"), display = "Pictograph (single icon)", + label.data.number.type = "Percentage", + maximum.value = ParseText("100%"), text.above = "Icon title") + expect_false(is.null(attr(res.icon, "ChartData"))) + expect_equal(attr(res.icon, "ChartLabels")$ChartTitle, "Icon title") + + # No ChartTitle exposed when text.above is empty (the default) + res.no.title <- VisualizeNumber(0.4, maximum.value = 1.0) + expect_null(attr(res.no.title, "ChartLabels")$ChartTitle) +}) From cdef02efd69c6277c603f084ec8e34d9b955a482 Mon Sep 17 00:00:00 2001 From: Justin Yap Date: Mon, 13 Jul 2026 15:35:22 +1000 Subject: [PATCH 2/2] RS-22593: Set chart attributes directly on the widget in VisualizeNumber Replace the wrapper helper with direct attribute assignment at each return path. The title to expose is computed once (NULL when no title is shown), so each path sets ChartData and ChartLabels on its widget without nesting calls. Co-Authored-By: Claude Opus 4.8 (1M context) --- R/visualizenumber.R | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/R/visualizenumber.R b/R/visualizenumber.R index e38397b..170b95a 100644 --- a/R/visualizenumber.R +++ b/R/visualizenumber.R @@ -186,17 +186,8 @@ VisualizeNumber <- function(x, # Snapshot the input value before any normalisation (e.g. the percentage # rescaling of x below) so it can be exposed unchanged as the chart's data. chart.data <- x - - # Attaches the visualized value as ChartData and the title (text.above) as - # ChartLabels$ChartTitle, so consumers can read the value and label it. - # Applied to whichever widget this function returns. - addChartAttributes <- function(widget) - { - attr(widget, "ChartData") <- chart.data - if (any(nzchar(text.above))) - attr(widget, "ChartLabels") <- list(ChartTitle = text.above) - widget - } + # Title to expose as the chart's data label, or NULL when no title is shown. + chart.labels <- if (any(nzchar(text.above))) list(ChartTitle = text.above) else NULL display <- switch(tolower(display), oval = "circle", circle = "circle", "number in an oval" = "circle", rectangle = "rectangle", square = "rectangle", "number in a rectangle" = "rectangle", @@ -314,7 +305,7 @@ VisualizeNumber <- function(x, } if (label.data.position == "None") label.str <- "" - return(addChartAttributes(iconsWithText(value, fill.icon.color = fill.color, + picto <- iconsWithText(value, fill.icon.color = fill.color, base.icon.color = base.color, maximum.value = maximum.value, total.icons = total.icons, ..., # other icon parameters? text.overlay = label.str, text.overlay.halign = tolower(label.data.halign), @@ -337,7 +328,10 @@ VisualizeNumber <- function(x, text.above.font.weight = tolower(text.above.font.weight), background.color = if (background.opacity > 0) background.color else "transparent", margin.top = margin.top, margin.right = margin.right, - margin.bottom = margin.bottom, margin.left = margin.left))) + margin.bottom = margin.bottom, margin.left = margin.left) + attr(picto, "ChartData") <- chart.data + attr(picto, "ChartLabels") <- chart.labels + return(picto) } if (display %in% c("donut", "gauge", "bar")) @@ -505,7 +499,9 @@ VisualizeNumber <- function(x, p$sizingPolicy$browser$padding <- 0 class(p) <- c(class(p), "visualization-selector") attr(p, "can-run-in-root-dom") <- TRUE - return(addChartAttributes(p)) + attr(p, "ChartData") <- chart.data + attr(p, "ChartLabels") <- chart.labels + return(p) } setText <- function(text, yalign, xalign, font, font.weight, # parameters always supplied