Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with Leaflet's addPolygons in bs4_book #1491

Open
4 of 5 tasks
jmhatch opened this issue Mar 8, 2025 · 4 comments
Open
4 of 5 tasks

Issue with Leaflet's addPolygons in bs4_book #1491

jmhatch opened this issue Mar 8, 2025 · 4 comments
Labels
bs4_book 🥾 bug an unexpected problem or unintended behavior external

Comments

@jmhatch
Copy link

jmhatch commented Mar 8, 2025

I have a leaflet map inside of a bs4_book that I'm rendering using bookdown. I've noticed that when I add 2+ (number can be different depending on data) addPolygons() to the leaflet map I get the following error message,

Tweaking _book/index.html
Error in if (xml2::xml_attr(parent, "class") == "row") { :
missing value where TRUE/FALSE needed
Please delete _main.Rmd after you finish debugging the error.

I can render this map in the browser or in RStudio's Viewer tab just fine. If the number of addPolyons() is less than 2, it'll render.

I try to render the book by running the following in the directory of the index.Rmd files, provided in the mwe.zip,

bookdown::render_book('index.Rmd', 'all')

Session info (but also fails in GH action when I install most recent packages):

R version 4.4.2 (2024-10-31 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 10 x64 (build 19045), RStudio 2023.12.1.402

Locale:
LC_COLLATE=English_United States.utf8 LC_CTYPE=English_United States.utf8
LC_MONETARY=English_United States.utf8 LC_NUMERIC=C
LC_TIME=English_United States.utf8

Package version:
base64enc_0.1.3 bookdown_0.41 bslib_0.9.0 cachem_1.1.0 cli_3.6.3
digest_0.6.37 evaluate_1.0.3 fastmap_1.2.0 fontawesome_0.5.3 fs_1.6.5
glue_1.8.0 graphics_4.4.2 grDevices_4.4.2 highr_0.11 htmltools_0.5.8.1
jquerylib_0.1.4 jsonlite_1.9.0 knitr_1.49 lifecycle_1.0.4 memoise_2.0.1
methods_4.4.2 mime_0.12 R6_2.6.1 rappdirs_0.3.3 rlang_1.1.4
rmarkdown_2.29 sass_0.4.9 stats_4.4.2 tinytex_0.55 tools_4.4.2
utils_4.4.2 xfun_0.51 yaml_2.3.10

Checklist

When filing a bug report, please check the boxes below to confirm that you have provided us with the information we need. Have you:

  • formatted your issue so it is easier for us to read?

  • included a minimal, self-contained, and reproducible example?

  • pasted the output from xfun::session_info('bookdown') in your issue?

  • upgraded all your packages to their latest versions (including your versions of R, the RStudio IDE, and relevant R packages)?

  • installed and tested your bug with the development version of the bookdown package using remotes::install_github("rstudio/bookdown") ?

@cderv
Copy link
Collaborator

cderv commented Mar 14, 2025

The error comes from some bs4_book() processing

bookdown/R/bs4_book.R

Lines 308 to 314 in 882e8d4

tweak_part_screwup <- function(html) {
sidebar <- xml2::xml_find_first(html, "//div[contains(@class, 'sidebar-chapter')]")
parent <- xml2::xml_parent(sidebar)
if (xml2::xml_attr(parent, "class") == "row") {
return()
}

Somehow, something makes the call xml2::xml_attr(parent, "class") == "row" not return TRUE / FALSE

That is what to look for

@cderv
Copy link
Collaborator

cderv commented Mar 14, 2025

I think the problem is that the file is too big for xml2 parser, but it does not throw any warning or error.

From the code above, this is what we get:

  • No sidebar is found

     > sidebar <- xml2::xml_find_first(html, "//div[contains(@class, 'sidebar-chapter')]")
     > sidebar
     {xml_missing}
     <NA>
  • Which is not expected the HTML file contains a sidebar

     </div>
       </main>
     
       <div class="col-md-3 col-lg-2 d-none d-md-block sidebar sidebar-chapter">
         <nav id="toc" data-toggle="toc" aria-label="On this page">
           <h2>On this page</h2>
           <div id="book-on-this-page"></div>
     
           <div class="book-extra">
             <ul class="list-unstyled">
               <li><a id="book-source" href="#">View source <i class="fab fa-github"></i></a></li>
               <li><a id="book-edit" href="#">Edit this page <i class="fab fa-github"></i></a></li>
             </ul>
           </div>
         </nav>
       </div>

    This is after main part and all the elments from the leaflet. So something in the leaflet output makes xml2 not working.

Somehow, it does not see the div after the second leaflet

> xml2::xml_find_all(html, "//div")
{xml_nodeset (11)}
 [1] <div class="container-fluid">\n<div class="row">\n   ...
 [2] <div class="row">\n  <header class="col-sm-12 col-lg ...
 [3] <div class="d-flex align-items-start justify-content ...
 [4] <div id="main-nav" class="collapse-lg">\n      <form ...
 [5] <div id="book-toc"></div>
 [6] <div class="book-extra">\n          <p><a id="book-r ...
 [7] <div id="illustrate-issue-w-leaflet" class="section  ...
 [8] <div id="works" class="section level3" number="2.0.1 ...
 [9] <div class="leaflet html-widget html-fill-item" id=" ...
[10] <div id="doesnt-work" class="section level3" number= ...
[11] <div class="leaflet html-widget html-fill-item" id=" ...

But it can find the two scripts for leaflet

> xml2::xml_find_all(html, "//script[contains(@data-for, 'htmlwidget')]")
{xml_nodeset (2)}
[1] <script type="application/json" data-for="htmlwidget- ...
[2] <script type="application/json" data-for="htmlwidget- ...

Something prevent the end of the file to be imported. It does not see any siblings after the <main> node

> xml2::xml_find_all(html, "//head/following-sibling::*")
{xml_nodeset (1)}
[1] <body data-spy="scroll" data-target="#toc">\r\n\r\n<d ...
> xml2::xml_find_all(html, "//main/following-sibling::*")
{xml_nodeset (0)}
Here is the full structure seen by xml2 - nothing after the last leaflet script

> xml2::xml_structure(html)
<html [lang]>
  <head>
    <meta [http-equiv, content]>
    <meta [charset]>
    <meta [name, content]>
    <title>
      {text}
    <meta [name, content]>
    <meta [name, content]>
    <meta [name, content]>
    <meta [property, content]>
    <meta [property, content]>
    <meta [property, content]>
    <meta [name, content]>
    <meta [name, content]>
    <meta [name, content]>
    {comment}
    <script [src, integrity, crossorigin]>
    <script [src, integrity, crossorigin]>
    <script [src, crossorigin]>
    <script [src]>
    <meta [name, content]>
    <link [href, rel]>
    <script [src]>
    <script [src]>
    <script [src]>
    <script [src]>
    <link [href, rel]>
    <script [src]>
    <link [href, rel]>
    <script [src]>
    <link [href, rel]>
    <script [src]>
    <link [href, rel]>
    <script [src]>
    <script [src]>
    <link [href, rel]>
    <script [src]>
    <script [src]>
    <script [src]>
    <script [src, integrity, crossorigin]>
    <script [src, integrity, crossorigin]>
    {comment}
    <style [type]>
      {cdata}
    <style [type]>
      {cdata}
    <link [rel, href]>
  <body [data-spy, data-target]>
    {text}
    <div [class]>
      {text}
      <div [class]>
        {text}
        <header [class]>
          <a [class, href]>
            {text}
          {text}
          <div [class]>
            {text}
            <h1>
              {text}
              <a [href, title]>
                {text}
              {text}
            {text}
            <button [class, type, data-toggle, data-target, aria-expanded, aria-controls]>
              <i [class]>
              <span [class]>
                {text}
            {text}
          {text}
          <div [id, class]>
            {text}
            <form [role]>
              {text}
              <input [id, class, type, placeholder, aria-label]>
            {text}
            <nav [aria-label]>
              <h2>
                {text}
              {text}
              <div [id]>
              {text}
              <div [class]>
                {text}
                <p>
                  <a [id, href]>
                    {text}
                    <i [class]>
                {text}
              {text}
          {text}
        <main [class, id]>
          <div [id, class, number]>
            {text}
            <h1>
              <span [class]>
                {text}
              {text}
            {text}
            <div [id, class, number]>
              {text}
              <h3>
                <span [class]>
                  {text}
                {text}
              {text}
              <div [class, id, style]>
              {text}
              <script [type, data-for]>
                {cdata}
            {text}
            <div [id, class, number]>
              {text}
              <h3>
                <span [class]>
                  {text}
                {text}
              {text}
              <div [class, id, style]>
              {text}
              <script [type, data-for]>
                {cdata}

So to make this work, xml2 needs to be fixed. In bookdown I am not sure how we should handle it.
Can we detect and throw an error? Do we protect against error by checking for no sidebar found, and just skipping the tweak? This would be wrong, obviously, but it would avoid errors.

@cderv
Copy link
Collaborator

cderv commented Mar 14, 2025

I have open issue in xml2

@cderv cderv added bs4_book 🥾 external bug an unexpected problem or unintended behavior labels Mar 14, 2025
@jmhatch
Copy link
Author

jmhatch commented Mar 26, 2025

@cderv Thanks for looking into this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bs4_book 🥾 bug an unexpected problem or unintended behavior external
Projects
None yet
Development

No branches or pull requests

2 participants