diff --git a/R/WorkbookClass.R b/R/WorkbookClass.R index a920b5c3..253438bc 100644 --- a/R/WorkbookClass.R +++ b/R/WorkbookClass.R @@ -2856,6 +2856,24 @@ Workbook$methods( values[1], values[2] ) + } else if (type == "containsBlanks") { + cfRule <- + sprintf( + ' + LEN(TRIM(%s))=0 + ', + dxfId, + unlist(strsplit(sqref, split = ":"))[[1]] + ) + } else if (type == "notContainsBlanks") { + cfRule <- + sprintf( + ' + LEN(TRIM(%s))>0 + ', + dxfId, + unlist(strsplit(sqref, split = ":"))[[1]] + ) } worksheets[[sheet]]$conditionalFormatting <<- diff --git a/R/conditional_formatting.R b/R/conditional_formatting.R index e2e51a3d..2876fef6 100644 --- a/R/conditional_formatting.R +++ b/R/conditional_formatting.R @@ -15,7 +15,7 @@ #' @param rule The condition under which to apply the formatting. See examples. #' @param style A style to apply to those cells that satisfy the rule. Default is createStyle(fontColour = "#9C0006", bgFill = "#FFC7CE") #' @param type Either 'expression', 'colourScale', 'databar', 'duplicates', 'beginsWith', -#' 'endsWith', 'topN', 'bottomN', 'contains' or 'notContains' (case insensitive). +#' 'endsWith', 'topN', 'bottomN', 'blanks', 'notBlanks', 'contains' or 'notContains' (case insensitive). #' @param ... See below #' @details See Examples. #' @@ -61,6 +61,18 @@ #' \item{style is a Style object. See [createStyle()]} #' \item{rule is a numeric vector of length 2 specifying lower and upper bound (Inclusive)} #' } +#' If type == "blanks" +#' \itemize{ +#' \item{style is a Style object. See [createStyle()]} +#' \item{rule is ignored.} +#' } +#' +#' If type == "notBlanks" +#' \itemize{ +#' \item{style is a Style object. See [createStyle()]} +#' \item{rule is ignored.} +#' } + #' #' If type == "topN" #' \itemize{ @@ -104,6 +116,8 @@ #' addWorksheet(wb, "between") #' addWorksheet(wb, "topN") #' addWorksheet(wb, "bottomN") +#' addWorksheet(wb, "containsBlanks") +#' addWorksheet(wb, "notContainsBlanks") #' addWorksheet(wb, "logical operators") #' #' negStyle <- createStyle(fontColour = "#9C0006", bgFill = "#FFC7CE") @@ -238,6 +252,18 @@ #' # Highlight bottom 20 percentage in column y #' conditionalFormatting(wb, "bottomN", cols = 2, rows = 2:11, #' style = negStyle, type = "topN", rank = 20, percent = TRUE) +#' +#' ## cells containing blanks +#' sample_data <- sample(c("X", NA_character_), 10, replace = TRUE) +#' writeData(wb, "containsBlanks", sample_data) +#' conditionalFormatting(wb, "containsBlanks", cols = 1, rows = 1:10, +#' type = "blanks", style = negStyle) +#' +#' ## cells not containing blanks +#' sample_data <- sample(c("X", NA_character_), 10, replace = TRUE) +#' writeData(wb, "notContainsBlanks", sample_data) +#' conditionalFormatting(wb, "notContainsBlanks", cols = 1, rows = 1:10, +#' type = "notBlanks", style = posStyle) #' #' ## Logical Operators #' # You can use Excels logical Operators @@ -320,9 +346,13 @@ conditionalFormatting <- type <- "topN" } else if (type == "bottomn") { type <- "bottomN" - } else if (type != "expression") { + } else if (type == "blanks") { + type <- "containsBlanks" + } else if (type == "notblanks") { + type <- "notContainsBlanks" + }else if (type != "expression") { stop( - "Invalid type argument. Type must be one of 'expression', 'colourScale', 'databar', 'duplicates', 'beginsWith', 'endsWith', 'contains' or 'notContains'" + "Invalid type argument. Type must be one of 'expression', 'colourScale', 'databar', 'duplicates', 'beginsWith', 'endsWith', 'blanks', 'notBlanks', 'contains' or 'notContains'" ) } @@ -629,6 +659,32 @@ conditionalFormatting <- invisible(dxfId <- wb$addDXFS(style)) values <- params rule <- style + } else if (type == "containsBlanks") { + # rule is ignored + + if (is.null(style)) { + style <- + createStyle(fontColour = "#9C0006", bgFill = "#FFC7CE") + } + + if (!"Style" %in% class(style)) { + stop("If type == 'blanks', style must be a Style object.") + } + + invisible(dxfId <- wb$addDXFS(style)) + } else if (type == "notContainsBlanks") { + # rule is ignored + + if (is.null(style)) { + style <- + createStyle(fontColour = "#9C0006", bgFill = "#FFC7CE") + } + + if (!"Style" %in% class(style)) { + stop("If type == 'notBlanks', style must be a Style object.") + } + + invisible(dxfId <- wb$addDXFS(style)) } diff --git a/man/conditionalFormatting.Rd b/man/conditionalFormatting.Rd index 19316d05..38907a63 100644 --- a/man/conditionalFormatting.Rd +++ b/man/conditionalFormatting.Rd @@ -30,7 +30,7 @@ conditionalFormatting( \item{style}{A style to apply to those cells that satisfy the rule. Default is createStyle(fontColour = "#9C0006", bgFill = "#FFC7CE")} \item{type}{Either 'expression', 'colourScale', 'databar', 'duplicates', 'beginsWith', -'endsWith', 'topN', 'bottomN', 'contains' or 'notContains' (case insensitive).} +'endsWith', 'topN', 'bottomN', 'blanks', 'notBlanks', 'contains' or 'notContains' (case insensitive).} \item{...}{See below} } @@ -82,6 +82,17 @@ If type == "between" \item{style is a Style object. See \code{\link[=createStyle]{createStyle()}}} \item{rule is a numeric vector of length 2 specifying lower and upper bound (Inclusive)} } +If type == "blanks" +\itemize{ +\item{style is a Style object. See \code{\link[=createStyle]{createStyle()}}} +\item{rule is ignored.} +} + +If type == "notBlanks" +\itemize{ +\item{style is a Style object. See \code{\link[=createStyle]{createStyle()}}} +\item{rule is ignored.} +} If type == "topN" \itemize{ @@ -123,6 +134,8 @@ addWorksheet(wb, "databar") addWorksheet(wb, "between") addWorksheet(wb, "topN") addWorksheet(wb, "bottomN") +addWorksheet(wb, "containsBlanks") +addWorksheet(wb, "notContainsBlanks") addWorksheet(wb, "logical operators") negStyle <- createStyle(fontColour = "#9C0006", bgFill = "#FFC7CE") @@ -257,6 +270,18 @@ conditionalFormatting(wb, "bottomN", cols = 1, rows = 2:11, # Highlight bottom 20 percentage in column y conditionalFormatting(wb, "bottomN", cols = 2, rows = 2:11, style = negStyle, type = "topN", rank = 20, percent = TRUE) + +## cells containing blanks +sample_data <- sample(c("X", NA_character_), 10, replace = TRUE) +writeData(wb, "containsBlanks", sample_data) +conditionalFormatting(wb, "containsBlanks", cols = 1, rows = 1:10, +type = "blanks", style = negStyle) + +## cells not containing blanks +sample_data <- sample(c("X", NA_character_), 10, replace = TRUE) +writeData(wb, "notContainsBlanks", sample_data) +conditionalFormatting(wb, "notContainsBlanks", cols = 1, rows = 1:10, +type = "notBlanks", style = posStyle) ## Logical Operators # You can use Excels logical Operators diff --git a/tests/testthat/test-conditionalFormatting.R b/tests/testthat/test-conditionalFormatting.R index 4efe169b..0150600b 100644 --- a/tests/testthat/test-conditionalFormatting.R +++ b/tests/testthat/test-conditionalFormatting.R @@ -57,3 +57,30 @@ test_that("topN/bottomN conditions correspond to 'top10' type", { expect_true(object = grepl(paste('type="top10"'), wb$worksheets[[1]]$conditionalFormatting[5])) expect_true(object = grepl(paste('type="top10"'), wb$worksheets[[1]]$conditionalFormatting[6])) }) + + +context("Testing 'blanks' and 'notBlanks' conditions in conditionalFormatting") +BNB_test_data <- data.frame(col1 = sample(c("X", NA_character_), 10, replace = TRUE), + col2 = sample(c("Y", NA_character_), 10, replace = TRUE)) + +bg_blue <- createStyle(bgFill = "skyblue") +bg_red <- createStyle(bgFill = "red") + +wb <- createWorkbook() +sht <- "Blanks_NonBlanks_TEST" +addWorksheet(wb, sht) +writeData(wb, sht, BNB_test_data) +conditionalFormatting(wb, sht, cols = 1, rows = 2:11, type = "blanks", style = bg_red) +conditionalFormatting(wb, sht, cols = 2, rows = 2:11, type = "notBlanks", style = bg_blue) + +test_that("Number of conditionalFormatting rules added equal to 2", { + expect_equal(object = length(wb$worksheets[[1]]$conditionalFormatting), expected = 2) +}) + +test_that("type='blanks' calls type='containsBlanks'", { + expect_true(object = grepl(paste('containsBlanks'), wb$worksheets[[1]]$conditionalFormatting[1])) +}) + +test_that("type='notBlanks' calls type='notContainsBlanks'", { + expect_true(object = grepl(paste('notContainsBlanks'), wb$worksheets[[1]]$conditionalFormatting[2])) +})