diff --git a/news/changelog-1.7.md b/news/changelog-1.7.md index c4941e75be2..b8e56fd8108 100644 --- a/news/changelog-1.7.md +++ b/news/changelog-1.7.md @@ -46,6 +46,7 @@ All changes included in 1.7: - ([#12277](https://github.com/quarto-dev/quarto-cli/pull/12277)): Provide light and dark plot and table renderings with `renderings: [light,dark]` - ([#11860](https://github.com/quarto-dev/quarto-cli/issues/11860)): ES6 modules that import other local JS modules in documents with `embed-resources: true` are now correctly embedded. - ([#1325](https://github.com/quarto-dev/quarto-cli/issues/1325)): Dark Mode pages should not flash light on reload. (Nor should Light Mode pages flash dark.) +- ([#12307](https://github.com/quarto-dev/quarto-cli/issues/12307)): Tabsets using `tabby.js` in non-boostrap html (`theme: pandoc`, `theme: none` or `minimal: true`) now correctly render reactive content when `server: shiny` is used. ## `pdf` format @@ -53,6 +54,10 @@ All changes included in 1.7: - ([#11903](https://github.com/quarto-dev/quarto-cli/issues/11903)): `crossref` configuration like `fig-title` or `tbl-title` now correctly supports multi word values, e.g. `fig-title: 'Supplementary Figure'`. - ([#11878](https://github.com/quarto-dev/quarto-cli/issues/11878), [#12085](https://github.com/quarto-dev/quarto-cli/issues/12085)): Correctly fixup raw LaTeX table having an unexpected table env with options (e.g `\begin{table}[!ht]`) to be handled as crossref table. +## `revealjs` format + +- ([#12307](https://github.com/quarto-dev/quarto-cli/issues/12307)): Tabsets using `tabby.js` in Revealjs now correctly render reactive content when `server: shiny` is used. + ### Quarto PDF engine - ([#12194](https://github.com/quarto-dev/quarto-cli/issues/12194)): More specific checks added in log parsing to automatically find missing fonts. diff --git a/src/resources/formats/html/quarto.js b/src/resources/formats/html/quarto.js index a71a420fce5..ee807684be1 100644 --- a/src/resources/formats/html/quarto.js +++ b/src/resources/formats/html/quarto.js @@ -66,19 +66,41 @@ window.document.addEventListener("DOMContentLoaded", function (_event) { } }; - // fire slideEnter for bootstrap tab activations (for htmlwidget resize behavior) - function fireSlideEnter(e) { + // dispatch for htmlwidgets + // they use slideenter event to trigger resize + function fireSlideEnter() { const event = window.document.createEvent("Event"); event.initEvent("slideenter", true, true); window.document.dispatchEvent(event); } + const tabs = window.document.querySelectorAll('a[data-bs-toggle="tab"]'); tabs.forEach((tab) => { tab.addEventListener("shown.bs.tab", fireSlideEnter); }); - // fire slideEnter for tabby tab activations (for htmlwidget resize behavior) - document.addEventListener("tabby", fireSlideEnter, false); + // dispatch for shiny + // they use BS shown and hidden events to trigger rendering + function distpatchShinyEvents(previous, current) { + if (window.jQuery) { + if (previous) { + window.jQuery(previous).trigger("hidden"); + } + if (current) { + window.jQuery(current).trigger("shown"); + } + } + } + + // tabby.js listener: Trigger event for htmlwidget and shiny + document.addEventListener( + "tabby", + function (event) { + fireSlideEnter(); + distpatchShinyEvents(event.detail.previousTab, event.detail.tab); + }, + false + ); // Track scrolling and mark TOC links as active // get table of contents and sidebar (bail if we don't have at least one) diff --git a/src/resources/formats/revealjs/plugins/support/support.js b/src/resources/formats/revealjs/plugins/support/support.js index 73246a7899f..20192919260 100644 --- a/src/resources/formats/revealjs/plugins/support/support.js +++ b/src/resources/formats/revealjs/plugins/support/support.js @@ -281,36 +281,49 @@ window.QuartoSupport = function () { } } + // dispatch for htmlwidgets + // they use slideenter event to trigger resize + const fireSlideEnter = () => { + const event = window.document.createEvent("Event"); + event.initEvent("slideenter", true, true); + window.document.dispatchEvent(event); + }; + + // dispatch for shiny + // they use BS shown and hidden events to trigger rendering + const distpatchShinyEvents = (previous, current) => { + if (window.jQuery) { + if (previous) { + window.jQuery(previous).trigger("hidden"); + } + if (current) { + window.jQuery(current).trigger("shown"); + } + } + }; + function handleSlideChanges(deck) { - // dispatch for htmlwidgets - const fireSlideEnter = () => { - const event = window.document.createEvent("Event"); - event.initEvent("slideenter", true, true); - window.document.dispatchEvent(event); - }; const fireSlideChanged = (previousSlide, currentSlide) => { fireSlideEnter(); - - // dispatch for shiny - if (window.jQuery) { - if (previousSlide) { - window.jQuery(previousSlide).trigger("hidden"); - } - if (currentSlide) { - window.jQuery(currentSlide).trigger("shown"); - } - } + distpatchShinyEvents(previousSlide, currentSlide); }; - // fire slideEnter for tabby tab activations (for htmlwidget resize behavior) - document.addEventListener("tabby", fireSlideEnter, false); - deck.on("slidechanged", function (event) { fireSlideChanged(event.previousSlide, event.currentSlide); }); } + function handleTabbyChanges() { + const fireTabChanged = (previousTab, currentTab) => { + fireSlideEnter() + distpatchShinyEvents(previousTab, currentTab); + }; + document.addEventListener("tabby", function(event) { + fireTabChanged(event.detail.previousTab, event.detail.tab); + }, false); + } + function workaroundMermaidDistance(deck) { if (window.document.querySelector("pre.mermaid-js")) { const slideCount = deck.getTotalSlides(); @@ -390,6 +403,7 @@ window.QuartoSupport = function () { addFooter(deck); addChalkboardButtons(deck); handleTabbyClicks(); + handleTabbyChanges(); handleSlideChanges(deck); workaroundMermaidDistance(deck); handleWhiteSpaceInColumns(deck); diff --git a/tests/docs/playwright/serve/shiny-r/tabby-html.qmd b/tests/docs/playwright/serve/shiny-r/tabby-html.qmd new file mode 100644 index 00000000000..6f488ff504d --- /dev/null +++ b/tests/docs/playwright/serve/shiny-r/tabby-html.qmd @@ -0,0 +1,44 @@ +--- +title: "Title" +format: html +server: shiny +_quarto: + playwright-test: serve/shiny-r/shiny-tabby-html.spec.ts +--- + +```{r} +#| context: server +output$text1 <- renderText({ + "hi, it's tab 1" +}) +output$text2 <- renderText({ + "hi, it's tab 2" +}) +output$text3 <- renderText({ + "hi, it's tab 3" +}) +``` + +## Slide + +::: {.panel-tabset} + +### tab1 + +```{r} +textOutput("text1") +``` + +### tab2 + +```{r} +textOutput("text2") +``` + +### tab3 + +```{r} +textOutput("text3") +``` + +::: \ No newline at end of file diff --git a/tests/docs/playwright/serve/shiny-r/tabby-reveal.qmd b/tests/docs/playwright/serve/shiny-r/tabby-reveal.qmd new file mode 100644 index 00000000000..4cee4de9a1c --- /dev/null +++ b/tests/docs/playwright/serve/shiny-r/tabby-reveal.qmd @@ -0,0 +1,44 @@ +--- +title: "Title" +format: revealjs +server: shiny +_quarto: + playwright-test: serve/shiny-r/shiny-tabby-reveal.spec.ts +--- + +```{r} +#| context: server +output$text1 <- renderText({ + "hi, it's tab 1" +}) +output$text2 <- renderText({ + "hi, it's tab 2" +}) +output$text3 <- renderText({ + "hi, it's tab 3" +}) +``` + +## Slide + +::: {.panel-tabset} + +### tab1 + +```{r} +textOutput("text1") +``` + +### tab2 + +```{r} +textOutput("text2") +``` + +### tab3 + +```{r} +textOutput("text3") +``` + +::: \ No newline at end of file diff --git a/tests/integration/playwright-tests.test.ts b/tests/integration/playwright-tests.test.ts index 8cba2e0f369..3b857b23f29 100644 --- a/tests/integration/playwright-tests.test.ts +++ b/tests/integration/playwright-tests.test.ts @@ -24,7 +24,7 @@ async function fullInit() { const globOutput = Deno.args.length ? expandGlobSync(Deno.args[0]) : expandGlobSync( - "docs/playwright/**/*.qmd", + "docs/playwright/!(serve|shiny)/**/*.qmd", ); setInitializer(fullInit);