diff --git a/DESCRIPTION b/DESCRIPTION index c716fece..c161f71a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: sandpaper Title: Create and Curate Carpentries Lessons -Version: 0.12.5 +Version: 0.13.0 Authors@R: c( person(given = "Zhian N.", family = "Kamvar", diff --git a/NEWS.md b/NEWS.md index 19899dc2..1fe7ea5f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,11 @@ -# sandpaper 0.12.5 (unreleased) +# sandpaper 0.13.0 (unreleased) + +## NEW FEATURES + +* The new `spoiler` class of fenced div will allow authors to specify an + expandable section of content that is collapsed by default. This replaces the + former paradigm of using "floating solution" blocks to present options for + installation on different platforms. (implemented: @tobyhodges, #502) ## BUG FIX diff --git a/inst/rmarkdown/lua/lesson.lua b/inst/rmarkdown/lua/lesson.lua index 68b47c3f..5de76169 100644 --- a/inst/rmarkdown/lua/lesson.lua +++ b/inst/rmarkdown/lua/lesson.lua @@ -17,6 +17,7 @@ local blocks = { ["checklist"] = "check-square", ["solution"] = "none", ["hint"] = "none", + ["spoiler"] = "eye", ["discussion"] = "message-circle", ["testimonial"] = "heart", ["keypoints"] = "key", @@ -31,6 +32,7 @@ local block_counts = { ["checklist"] = 0, ["solution"] = 0, ["hint"] = 0, + ["spoiler"] = 0, ["discussion"] = 0, ["testimonial"] = 0, ["keypoints"] = 0, @@ -185,12 +187,18 @@ local button_headings = {

{{title}}

]], + ["spoiler"] = [[ +

+
+ {{title}} +

]], } local accordion_titles = { ["instructor"] = "Instructor Note", ["hint"] = "Give me a hint", - ["solution"] = "Show me the solution" + ["solution"] = "Show me the solution", + ["spoiler"] = "Show details" } local accordion_button = [[ @@ -250,7 +258,9 @@ accordion = function(el, class) -- the whole package local main_div = pandoc.Div({accordion_item}) local main_class = {"accordion", "instructor-note", "accordion-flush"} - if class ~= "instructor" then + if class == "spoiler" then + main_class[2] = "spoiler-accordion" + elseif class ~= "instructor" then main_class[2] = "challenge-accordion" end main_div.identifier = div_id @@ -288,12 +298,12 @@ end challenge_block = function(el) -- The challenge blocks no longer contain solutions nested inside. Instead, - -- the soltuions (and hints) are piled at the end of the block, so series of + -- the solutions (and hints) are piled at the end of the block, so series of -- challenge/solutions need to be separated. -- The challenge train is a list to contain all the divs local challenge_train = pandoc.List:new() - -- If the challenge contains multipl solutions or hints, we need to indicate + -- If the challenge contains multiple solutions or hints, we need to indicate -- that the following challenges/solutions are continuations. local this_head = get_header(el, 3) local next_head = this_head:clone() @@ -357,16 +367,22 @@ handle_our_divs = function(el) -- Accordion blocks: -- - -- Instructor Notes, Solutions, and Hints are all blocks that are contained in - -- accordion blocks. For historical reasons, solutions are normally embedded - -- in challenge blocks, but because of the way pandoc traverses the AST, we - -- need to process these FIRST and then handle their positioning in the + -- Instructor Notes, Solutions, Hints, and Spoilers are all blocks + -- that are contained in accordion blocks. + -- For historical reasons, solutions are normally embedded + -- in challenge blocks, but because of the way pandoc traverses the AST, + -- we need to process these FIRST and then handle their positioning in the -- challenge block phase. v,i = el.classes:find("instructor") if i ~= nil then return(accordion(el, "instructor")) end + v,i = el.classes:find("spoiler") + if i ~= nil then + return(accordion(el, "spoiler")) + end + v,i = el.classes:find("solution") if i ~= nil then return(accordion(el, "solution")) diff --git a/tests/testthat/_snaps/render_html.md b/tests/testthat/_snaps/render_html.md index b402e4a2..2c70b003 100644 --- a/tests/testthat/_snaps/render_html.md +++ b/tests/testthat/_snaps/render_html.md @@ -170,8 +170,8 @@ , Div ( "collapseSolution1" , [ "accordion-collapse" , "collapse" ] - , [ ( "[solution collapse]" ) - , ( "[solution collapse]" ) + , [ ( "[Solution hidden]" ) + , ( "[Solution hidden]" ) ] ) [ Div @@ -202,8 +202,8 @@ , Div ( "collapseInstructor1" , [ "accordion-collapse" , "collapse" ] - , [ ( "[instructor collapse]" ) - , ( "[instructor collapse]" ) + , [ ( "[Instructor hidden]" ) + , ( "[Instructor hidden]" ) ] ) [ Div @@ -221,6 +221,48 @@ ] ] ] + , Div + ( "accordionSpoiler1" + , [ "accordion" , "spoiler-accordion" , "accordion-flush" ] + , [] + ) + [ Div + ( "" , [ "accordion-item" ] , [] ) + [ RawBlock + (Format "html") + "" + , Div + ( "collapseSpoiler1" + , [ "accordion-collapse" , "collapse" ] + , [ ( "[Spoiler hidden]" ) + , ( "[Spoiler hidden]" ) + ] + ) + [ Div + ( "" , [ "accordion-body" ] , [] ) + [ Para + [ Str "That" + , Space + , Str "fin" + , Space + , Str "on" + , Space + , Str "the" + , Space + , Str "rear" + , Space + , Str "end" + , Space + , Str "of" + , Space + , Str "a" + , Space + , Str "car" + ] + ] + ] + ] + ] , Div ( "" , [ "nothing" ] , [] ) [ Para @@ -364,8 +406,8 @@ Write now - [solution collapse] - [solution collapse] +

just write it, silly.

@@ -381,15 +423,32 @@ Instructor Note - [instructor collapse] - [instructor collapse] - [instructor collapse] +

This should be aside

+
+
+ +
+
+

That fin on the rear end of a car

+
+
+
+

This should be

diff --git a/tests/testthat/examples/ex.md b/tests/testthat/examples/ex.md index bc67f8e5..55f0672d 100644 --- a/tests/testthat/examples/ex.md +++ b/tests/testthat/examples/ex.md @@ -45,6 +45,12 @@ This should be aside ::: +::: spoiler + +That fin on the rear end of a car + +::: + ::: nothing This should be diff --git a/tests/testthat/test-render_html.R b/tests/testthat/test-render_html.R index 57d7e67d..a5878e85 100644 --- a/tests/testthat/test-render_html.R +++ b/tests/testthat/test-render_html.R @@ -89,8 +89,8 @@ test_that("pandoc structure is rendered correctly", { } skip_on_os("windows") formation = function(x) { - x <- sub("(data-bs-parent|aria-labelledby).+?Instructor1", "[instructor collapse]", x) - sub("(data-bs-parent|aria-labelledby).+?Solution1", "[solution collapse]", x) + rgx <- "(data-bs-parent|aria-labelledby).+?(Instructor|Solution|Spoiler)1" + return(sub(rgx, "[\\2 hidden]", x)) } expect_snapshot(cat(readLines(out), sep = "\n"), transform = formation) }) @@ -147,11 +147,13 @@ test_that("render_html applies the internal lua filter", { } skip_on_os("windows") formation = function(x) { - x <- sub("[<]div id[=]\"collapseSolution1\".+", "[solution collapse]", x) - x <- sub("[<]div id[=]\"collapseInstructor1\".+", "[instructor collapse]", x) - x <- sub("(data-bs-parent|aria-labelledby).+?Instructor1.+$", "[instructor collapse]", x) - sub("(data-bs-parent|aria-labelledby).+?Solution1.+$", "[solution collapse]", x) - + open <- "[<]div id[=]\"collapse(Instructor|Solution|Spoiler)\\d\".+" + mid <- "(data-bs-parent|aria-labelledby).+?(Instructor|Solution|Spoiler)\\d[\"]$" + close <- "(data-bs-parent|aria-labelledby).+?(Instructor|Solution|Spoiler)\\d.+[>]$" + x <- sub(open, "
", x) + return(x) } expect_snapshot(cat(res), transform = formation) })