@undrr/undrr-mangrove 1.3.3 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/README.md +33 -29
  2. package/components/BarChart.js +2 -2
  3. package/components/BookCard.js +6 -0
  4. package/components/BookCard.js.LICENSE.txt +11 -0
  5. package/components/Breadcrumbs.js +6 -0
  6. package/components/Breadcrumbs.js.LICENSE.txt +9 -0
  7. package/components/Checkbox.js +6 -0
  8. package/components/Checkbox.js.LICENSE.txt +9 -0
  9. package/components/Chips.js +6 -0
  10. package/components/Chips.js.LICENSE.txt +9 -0
  11. package/components/CtaButton.js +6 -0
  12. package/components/CtaButton.js.LICENSE.txt +9 -0
  13. package/components/EmbedContainer.js +6 -0
  14. package/components/EmbedContainer.js.LICENSE.txt +9 -0
  15. package/components/Fetcher.js +2 -2
  16. package/components/Footer.js +6 -0
  17. package/components/Footer.js.LICENSE.txt +9 -0
  18. package/components/FormErrorSummary.js +6 -0
  19. package/components/FormErrorSummary.js.LICENSE.txt +9 -0
  20. package/components/FormGroup.js +6 -0
  21. package/components/FormGroup.js.LICENSE.txt +9 -0
  22. package/components/FullWidth.js +6 -0
  23. package/components/FullWidth.js.LICENSE.txt +9 -0
  24. package/components/Gallery.js +1 -1
  25. package/components/Hero.js +6 -0
  26. package/components/Hero.js.LICENSE.txt +9 -0
  27. package/components/HighlightBox.js +6 -0
  28. package/components/HighlightBox.js.LICENSE.txt +9 -0
  29. package/components/HorizontalBookCard.js +6 -0
  30. package/components/HorizontalBookCard.js.LICENSE.txt +11 -0
  31. package/components/HorizontalCard.js +6 -0
  32. package/components/HorizontalCard.js.LICENSE.txt +11 -0
  33. package/components/IconCard.js +2 -2
  34. package/components/Loader.js +6 -0
  35. package/components/Loader.js.LICENSE.txt +9 -0
  36. package/components/MapComponent.js +2 -2
  37. package/components/MegaMenu.js +2 -2
  38. package/components/PageHeader.js +6 -0
  39. package/components/PageHeader.js.LICENSE.txt +9 -0
  40. package/components/Pager.js +2 -2
  41. package/components/QuoteHighlight.js +1 -1
  42. package/components/Radio.js +6 -0
  43. package/components/Radio.js.LICENSE.txt +9 -0
  44. package/components/ScrollContainer.js +1 -1
  45. package/components/Select.js +6 -0
  46. package/components/Select.js.LICENSE.txt +9 -0
  47. package/components/ShareButtons.js +1 -1
  48. package/components/ShowMore.js +6 -0
  49. package/components/ShowMore.js.LICENSE.txt +9 -0
  50. package/components/StatsCard.js +2 -2
  51. package/components/SyndicationSearchWidget.js +2 -2
  52. package/components/Tab.js +6 -0
  53. package/components/Tab.js.LICENSE.txt +9 -0
  54. package/components/TextCta.js +6 -0
  55. package/components/TextCta.js.LICENSE.txt +11 -0
  56. package/components/TextInput.js +6 -0
  57. package/components/TextInput.js.LICENSE.txt +9 -0
  58. package/components/Textarea.js +6 -0
  59. package/components/Textarea.js.LICENSE.txt +9 -0
  60. package/components/VerticalCard.js +6 -0
  61. package/components/VerticalCard.js.LICENSE.txt +11 -0
  62. package/components/hydrate.js +1 -1
  63. package/css/style-delta.css +467 -0
  64. package/css/style-delta.css.map +1 -0
  65. package/css/style-gutenberg.css +21 -18
  66. package/css/style-gutenberg.css.map +1 -1
  67. package/css/style-irp-legacy.css +472 -0
  68. package/css/style-irp-legacy.css.map +1 -0
  69. package/css/style-irp.css +99 -103
  70. package/css/style-irp.css.map +1 -1
  71. package/css/style-legacy.css +465 -0
  72. package/css/style-legacy.css.map +1 -0
  73. package/css/style-mcr-legacy.css +472 -0
  74. package/css/style-mcr-legacy.css.map +1 -0
  75. package/css/style-mcr.css +99 -103
  76. package/css/style-mcr.css.map +1 -1
  77. package/css/style-preventionweb-legacy.css +472 -0
  78. package/css/style-preventionweb-legacy.css.map +1 -0
  79. package/css/style-preventionweb.css +99 -103
  80. package/css/style-preventionweb.css.map +1 -1
  81. package/css/style.css +99 -103
  82. package/css/style.css.map +1 -1
  83. package/error-pages/401.html +10 -11
  84. package/error-pages/403.html +11 -12
  85. package/error-pages/404.html +13 -14
  86. package/error-pages/429.html +12 -13
  87. package/error-pages/500.html +10 -11
  88. package/error-pages/502.html +12 -13
  89. package/error-pages/503.html +12 -13
  90. package/error-pages/504.html +10 -11
  91. package/error-pages/5xx.html +10 -11
  92. package/error-pages/challenge.html +12 -13
  93. package/error-pages/managed-challenge.html +12 -13
  94. package/js/README.md +57 -0
  95. package/js/__tests__/show-more.test.js +76 -0
  96. package/js/__tests__/table-of-contents.test.js +198 -0
  97. package/js/on-this-page-nav.js +531 -0
  98. package/js/show-more.js +24 -3
  99. package/js/table-of-contents.js +168 -0
  100. package/js/tabs.js +445 -83
  101. package/package.json +1 -1
  102. package/scss/Atom/BaseTypography/Blockquote/blockquote.scss +1 -8
  103. package/scss/Atom/BaseTypography/Cite/cite.scss +2 -2
  104. package/scss/Atom/Images/AuthorImage/author-image.scss +4 -4
  105. package/scss/Atom/Images/ImageCaptionCredit/image-caption-credit.scss +24 -28
  106. package/scss/Atom/Images/ImageCredit/image-credit.scss +1 -1
  107. package/scss/Atom/Layout/Container/container.scss +2 -2
  108. package/scss/Atom/Layout/Grid/grid.scss +1 -1
  109. package/scss/Atom/ReachElement/Details/details.scss +6 -6
  110. package/scss/Atom/ReachElement/Figcaption/figcaption.scss +1 -1
  111. package/scss/Atom/Table/table.scss +0 -8
  112. package/scss/Components/Boilerplate/boilerplate.scss +2 -2
  113. package/scss/Components/Breadcrumbs/breadcrumbs.scss +2 -9
  114. package/scss/Components/Buttons/Chips/chips.scss +5 -19
  115. package/scss/Components/Buttons/CtaButton/buttons.scss +3 -124
  116. package/scss/Components/Buttons/CtaButton/cta-button.scss +99 -0
  117. package/scss/Components/Buttons/ShareButtons/share-buttons.scss +2 -2
  118. package/scss/Components/Cards/Card/card.scss +45 -6
  119. package/scss/Components/Cards/StatsCard/stats-card.scss +2 -2
  120. package/scss/Components/ErrorPages/error-pages.scss +12 -12
  121. package/scss/Components/Footer/footer.scss +72 -4
  122. package/scss/Components/Forms/Select/select.scss +2 -2
  123. package/scss/Components/Forms/_form-base.scss +5 -5
  124. package/scss/Components/Forms/_form-legacy.scss +1 -1
  125. package/scss/Components/Gallery/gallery.scss +7 -7
  126. package/scss/Components/Hero/hero.scss +43 -23
  127. package/scss/Components/HighlightBox/highlight-box.scss +5 -5
  128. package/scss/Components/MegaMenu/mega-menu.scss +750 -0
  129. package/scss/Components/MegaMenu/megamenu.scss +3 -733
  130. package/scss/Components/OnThisPageNav/on-this-page-nav.scss +206 -0
  131. package/scss/Components/PageHeader/page-header.scss +5 -5
  132. package/scss/Components/Pagination/pagination.scss +2 -2
  133. package/scss/Components/ScrollContainer/scroll-container.scss +1 -1
  134. package/scss/Components/Snackbar/snackbar.scss +1 -1
  135. package/scss/Components/SyndicationSearchWidget/SyndicationSearchWidget.scss +3 -1480
  136. package/scss/Components/SyndicationSearchWidget/syndication-search-widget.scss +1515 -0
  137. package/scss/Components/Tab/tab.scss +68 -9
  138. package/scss/Components/TableOfContents/TableOfContents.scss +3 -17
  139. package/scss/Components/TableOfContents/table-of-contents.scss +17 -0
  140. package/scss/Components/TextCta/text-cta.scss +139 -0
  141. package/scss/Components/TextCta/textcta.scss +3 -27
  142. package/scss/Molecules/ImageCaption/image-caption.scss +6 -16
  143. package/scss/Molecules/SectionHeader/section-header.scss +8 -0
  144. package/scss/Molecules/SectionHeader/sectionheader.scss +3 -8
  145. package/scss/Utilities/FullWidth/FullWidth.scss +3 -23
  146. package/scss/Utilities/FullWidth/full-width.scss +23 -0
  147. package/scss/Utilities/Loader/loader.scss +1 -1
  148. package/scss/Utilities/ShowMore/ShowMore.scss +3 -26
  149. package/scss/Utilities/ShowMore/show-more.scss +26 -0
  150. package/scss/assets/scss/_components.scss +15 -9
  151. package/scss/assets/scss/_foundational.scss +13 -7
  152. package/scss/assets/scss/_mixins.scss +9 -314
  153. package/scss/assets/scss/_utility.scss +19 -71
  154. package/scss/assets/scss/_variables-delta.scss +110 -0
  155. package/scss/assets/scss/_variables-irp.scss +1 -1
  156. package/scss/assets/scss/_variables-mcr.scss +1 -1
  157. package/scss/assets/scss/_variables-preventionweb.scss +1 -1
  158. package/scss/assets/scss/_variables.scss +101 -34
  159. package/scss/assets/scss/style-delta.scss +8 -0
  160. package/scss/assets/scss/style-gutenberg.scss +2 -2
  161. package/scss/assets/scss/style-irp-legacy.scss +20 -0
  162. package/scss/assets/scss/style-legacy.scss +20 -0
  163. package/scss/assets/scss/style-mcr-legacy.scss +20 -0
  164. package/scss/assets/scss/style-preventionweb-legacy.scss +20 -0
  165. package/scss/Components/BlockquoteComponent/blockquotecomp.scss +0 -31
  166. package/scss/Components/Buttons/CtaLink/cta-link.scss +0 -61
@@ -26,25 +26,24 @@
26
26
  <style>
27
27
  /* Critical inline styles for immediate render */
28
28
  *,*::before,*::after{box-sizing:border-box}
29
- html{font-size:10px}
30
- body{font-family:system-ui,-apple-system,sans-serif;font-size:1.6rem;line-height:1.5;margin:0;color:#1a1a1a}
31
- h1,h2{margin:1rem 0;font-weight:700}
32
- h1{font-size:1.4rem;color:#004f91;margin:0}
33
- h2{font-size:2.3rem;margin-top:.5rem}
29
+ body{font-family:system-ui,-apple-system,sans-serif;font-size:16px;line-height:1.5;margin:0;color:#1a1a1a}
30
+ h1,h2{margin:10px 0;font-weight:700}
31
+ h1{font-size:14px;color:#004f91;margin:0}
32
+ h2{font-size:23px;margin-top:5px}
34
33
  a{color:#004f91;text-decoration:none}
35
34
  a:hover{text-decoration:underline}
36
35
  hr{border:1px solid #b3b3b3}
37
- small{display:block;margin-bottom:.5rem}
38
- .mg-error-page{min-height:100vh;background:#004f91;padding-bottom:2rem}
36
+ small{display:block;margin-bottom:5px}
37
+ .mg-error-page{min-height:100vh;background:#004f91;padding-bottom:20px}
39
38
  .mg-page-header__decoration{display:flex}
40
39
  .mg-page-header__decoration div{width:25%;height:7px}
41
40
  .mg-page-header__decoration div:nth-child(1){background:#c10920}
42
41
  .mg-page-header__decoration div:nth-child(2){background:#962987}
43
42
  .mg-page-header__decoration div:nth-child(3){background:#eb752a}
44
43
  .mg-page-header__decoration div:nth-child(4){background:#00afae}
45
- .mg-error-page__container{background:#fff;padding:2rem 1.5rem 1rem;max-width:900px;margin:2rem auto}
46
- .mg-error-page__actions{display:flex;gap:1rem;flex-wrap:wrap;margin:1.5rem 0}
47
- .mg-button{display:inline-flex;align-items:center;justify-content:center;padding:1rem 1.5rem;font-size:1.4rem;font-weight:600;color:#fff;background:#004f91;border:1px solid #fff;cursor:pointer;text-decoration:none}
44
+ .mg-error-page__container{background:#fff;padding:20px 15px 10px;max-width:900px;margin:20px auto}
45
+ .mg-error-page__actions{display:flex;gap:10px;flex-wrap:wrap;margin:15px 0}
46
+ .mg-button{display:inline-flex;align-items:center;justify-content:center;padding:10px 15px;font-size:14px;font-weight:600;color:#fff;background:#004f91;border:1px solid #fff;cursor:pointer;text-decoration:none}
48
47
  .mg-button:hover{background:#3372a7;color:#fff;text-decoration:none}
49
48
  .mg-button-secondary{background:#4d84b2}
50
49
  .mg-button-secondary:hover{background:#80a7c8}
@@ -88,7 +87,7 @@
88
87
  document.head.appendChild(el);
89
88
  }
90
89
  // Load full CSS from CDN (includes fonts)
91
- load('link',{rel:'stylesheet',href:'https://assets.undrr.org/static/mangrove/1.3.3/css/style.css'});
90
+ load('link',{rel:'stylesheet',href:'https://assets.undrr.org/static/mangrove/1.4.1/css/style.css'});
92
91
  // Load analytics
93
92
  load('script',{src:'https://assets.undrr.org/static/analytics/v1.0.0/google_analytics_enhancements.js',crossorigin:'anonymous'});
94
93
  load('script',{src:'https://messaging.undrr.org/src/undrr-messaging.js',defer:''});
@@ -29,25 +29,24 @@
29
29
  <style>
30
30
  /* Critical inline styles for immediate render */
31
31
  *,*::before,*::after{box-sizing:border-box}
32
- html{font-size:10px}
33
- body{font-family:system-ui,-apple-system,sans-serif;font-size:1.6rem;line-height:1.5;margin:0;color:#1a1a1a}
34
- h1,h2{margin:1rem 0;font-weight:700}
35
- h1{font-size:1.4rem;color:#004f91;margin:0}
36
- h2{font-size:2.3rem;margin-top:.5rem}
32
+ body{font-family:system-ui,-apple-system,sans-serif;font-size:16px;line-height:1.5;margin:0;color:#1a1a1a}
33
+ h1,h2{margin:10px 0;font-weight:700}
34
+ h1{font-size:14px;color:#004f91;margin:0}
35
+ h2{font-size:23px;margin-top:5px}
37
36
  a{color:#004f91;text-decoration:none}
38
37
  a:hover{text-decoration:underline}
39
38
  hr{border:1px solid #b3b3b3}
40
- small{display:block;margin-bottom:.5rem}
41
- .mg-error-page{min-height:100vh;background:#004f91;padding-bottom:2rem}
39
+ small{display:block;margin-bottom:5px}
40
+ .mg-error-page{min-height:100vh;background:#004f91;padding-bottom:20px}
42
41
  .mg-page-header__decoration{display:flex}
43
42
  .mg-page-header__decoration div{width:25%;height:7px}
44
43
  .mg-page-header__decoration div:nth-child(1){background:#c10920}
45
44
  .mg-page-header__decoration div:nth-child(2){background:#962987}
46
45
  .mg-page-header__decoration div:nth-child(3){background:#eb752a}
47
46
  .mg-page-header__decoration div:nth-child(4){background:#00afae}
48
- .mg-error-page__container{background:#fff;padding:2rem 1.5rem 1rem;max-width:900px;margin:2rem auto}
49
- .mg-error-page__actions{display:flex;gap:1rem;flex-wrap:wrap;margin:1.5rem 0}
50
- .mg-button{display:inline-flex;align-items:center;justify-content:center;padding:1rem 1.5rem;font-size:1.4rem;font-weight:600;color:#fff;background:#004f91;border:1px solid #fff;cursor:pointer;text-decoration:none}
47
+ .mg-error-page__container{background:#fff;padding:20px 15px 10px;max-width:900px;margin:20px auto}
48
+ .mg-error-page__actions{display:flex;gap:10px;flex-wrap:wrap;margin:15px 0}
49
+ .mg-button{display:inline-flex;align-items:center;justify-content:center;padding:10px 15px;font-size:14px;font-weight:600;color:#fff;background:#004f91;border:1px solid #fff;cursor:pointer;text-decoration:none}
51
50
  .mg-button:hover{background:#3372a7;color:#fff;text-decoration:none}
52
51
  .mg-button-secondary{background:#4d84b2}
53
52
  .mg-button-secondary:hover{background:#80a7c8}
@@ -91,7 +90,7 @@
91
90
  document.head.appendChild(el);
92
91
  }
93
92
  // Load full CSS from CDN (includes fonts)
94
- load('link',{rel:'stylesheet',href:'https://assets.undrr.org/static/mangrove/1.3.3/css/style.css'});
93
+ load('link',{rel:'stylesheet',href:'https://assets.undrr.org/static/mangrove/1.4.1/css/style.css'});
95
94
  // Load analytics
96
95
  load('script',{src:'https://assets.undrr.org/static/analytics/v1.0.0/google_analytics_enhancements.js',crossorigin:'anonymous'});
97
96
  load('script',{src:'https://messaging.undrr.org/src/undrr-messaging.js',defer:''});
@@ -29,28 +29,27 @@
29
29
  <style>
30
30
  /* Critical inline styles for immediate render */
31
31
  *,*::before,*::after{box-sizing:border-box}
32
- html{font-size:10px}
33
- body{font-family:system-ui,-apple-system,sans-serif;font-size:1.6rem;line-height:1.5;margin:0;color:#1a1a1a}
34
- h1,h2{margin:1rem 0;font-weight:700}
35
- h1{font-size:1.4rem;color:#004f91;margin:0}
36
- h2{font-size:2.3rem;margin-top:.5rem}
32
+ body{font-family:system-ui,-apple-system,sans-serif;font-size:16px;line-height:1.5;margin:0;color:#1a1a1a}
33
+ h1,h2{margin:10px 0;font-weight:700}
34
+ h1{font-size:14px;color:#004f91;margin:0}
35
+ h2{font-size:23px;margin-top:5px}
37
36
  a{color:#004f91;text-decoration:none}
38
37
  a:hover{text-decoration:underline}
39
38
  hr{border:1px solid #b3b3b3}
40
- small{display:block;margin-bottom:.5rem}
41
- .mg-error-page{min-height:100vh;background:#004f91;padding-bottom:2rem}
39
+ small{display:block;margin-bottom:5px}
40
+ .mg-error-page{min-height:100vh;background:#004f91;padding-bottom:20px}
42
41
  .mg-error-page--challenge{background:#f2f2f2}
43
- .mg-error-page--challenge .mg-error-page__container{text-align:center;padding:3rem 2.5rem 2rem;margin-top:4rem;max-width:540px;border-radius:8px;box-shadow:0 2px 12px rgb(0 0 0 / 8%)}
44
- .mg-error-page--challenge .undrr-logo{width:100%;margin:0 0 2.5rem;background-position:center;height:35px}
45
- .mg-error-page--challenge h1{font-size:2.3rem;font-weight:600;color:#1a1a1a;margin:0 0 .5rem}
42
+ .mg-error-page--challenge .mg-error-page__container{text-align:center;padding:30px 25px 20px;margin-top:40px;max-width:540px;border-radius:8px;box-shadow:0 2px 12px rgb(0 0 0 / 8%)}
43
+ .mg-error-page--challenge .undrr-logo{width:100%;margin:0 0 25px;background-position:center;height:35px}
44
+ .mg-error-page--challenge h1{font-size:23px;font-weight:600;color:#1a1a1a;margin:0 0 5px}
46
45
  .mg-page-header__decoration{display:flex}
47
46
  .mg-page-header__decoration div{width:25%;height:7px}
48
47
  .mg-page-header__decoration div:nth-child(1){background:#c10920}
49
48
  .mg-page-header__decoration div:nth-child(2){background:#962987}
50
49
  .mg-page-header__decoration div:nth-child(3){background:#eb752a}
51
50
  .mg-page-header__decoration div:nth-child(4){background:#00afae}
52
- .mg-error-page__container{background:#fff;padding:2rem 1.5rem 1rem;max-width:900px;margin:2rem auto}
53
- .mg-button{display:inline-flex;align-items:center;justify-content:center;padding:1rem 1.5rem;font-size:1.4rem;font-weight:600;color:#fff;background:#004f91;border:1px solid #fff;cursor:pointer;text-decoration:none}
51
+ .mg-error-page__container{background:#fff;padding:20px 15px 10px;max-width:900px;margin:20px auto}
52
+ .mg-button{display:inline-flex;align-items:center;justify-content:center;padding:10px 15px;font-size:14px;font-weight:600;color:#fff;background:#004f91;border:1px solid #fff;cursor:pointer;text-decoration:none}
54
53
  .mg-button:hover{background:#3372a7;color:#fff;text-decoration:none}
55
54
  .mg-u-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(0);border:0}
56
55
  </style>
@@ -89,7 +88,7 @@
89
88
  document.head.appendChild(el);
90
89
  }
91
90
  // Load full CSS from CDN (includes fonts)
92
- load('link',{rel:'stylesheet',href:'https://assets.undrr.org/static/mangrove/1.3.3/css/style.css'});
91
+ load('link',{rel:'stylesheet',href:'https://assets.undrr.org/static/mangrove/1.4.1/css/style.css'});
93
92
  // Load analytics
94
93
  load('script',{src:'https://assets.undrr.org/static/analytics/v1.0.0/google_analytics_enhancements.js',crossorigin:'anonymous'});
95
94
  load('script',{src:'https://messaging.undrr.org/src/undrr-messaging.js',defer:''});
@@ -31,28 +31,27 @@
31
31
  <style>
32
32
  /* Critical inline styles for immediate render */
33
33
  *,*::before,*::after{box-sizing:border-box}
34
- html{font-size:10px}
35
- body{font-family:system-ui,-apple-system,sans-serif;font-size:1.6rem;line-height:1.5;margin:0;color:#1a1a1a}
36
- h1,h2{margin:1rem 0;font-weight:700}
37
- h1{font-size:1.4rem;color:#004f91;margin:0}
38
- h2{font-size:2.3rem;margin-top:.5rem}
34
+ body{font-family:system-ui,-apple-system,sans-serif;font-size:16px;line-height:1.5;margin:0;color:#1a1a1a}
35
+ h1,h2{margin:10px 0;font-weight:700}
36
+ h1{font-size:14px;color:#004f91;margin:0}
37
+ h2{font-size:23px;margin-top:5px}
39
38
  a{color:#004f91;text-decoration:none}
40
39
  a:hover{text-decoration:underline}
41
40
  hr{border:1px solid #b3b3b3}
42
- small{display:block;margin-bottom:.5rem}
43
- .mg-error-page{min-height:100vh;background:#004f91;padding-bottom:2rem}
41
+ small{display:block;margin-bottom:5px}
42
+ .mg-error-page{min-height:100vh;background:#004f91;padding-bottom:20px}
44
43
  .mg-error-page--challenge{background:#f2f2f2}
45
- .mg-error-page--challenge .mg-error-page__container{text-align:center;padding:3rem 2.5rem 2rem;margin-top:4rem;max-width:540px;border-radius:8px;box-shadow:0 2px 12px rgb(0 0 0 / 8%)}
46
- .mg-error-page--challenge .undrr-logo{width:100%;margin:0 0 2.5rem;background-position:center;height:35px}
47
- .mg-error-page--challenge h1{font-size:2.3rem;font-weight:600;color:#1a1a1a;margin:0 0 .5rem}
44
+ .mg-error-page--challenge .mg-error-page__container{text-align:center;padding:30px 25px 20px;margin-top:40px;max-width:540px;border-radius:8px;box-shadow:0 2px 12px rgb(0 0 0 / 8%)}
45
+ .mg-error-page--challenge .undrr-logo{width:100%;margin:0 0 25px;background-position:center;height:35px}
46
+ .mg-error-page--challenge h1{font-size:23px;font-weight:600;color:#1a1a1a;margin:0 0 5px}
48
47
  .mg-page-header__decoration{display:flex}
49
48
  .mg-page-header__decoration div{width:25%;height:7px}
50
49
  .mg-page-header__decoration div:nth-child(1){background:#c10920}
51
50
  .mg-page-header__decoration div:nth-child(2){background:#962987}
52
51
  .mg-page-header__decoration div:nth-child(3){background:#eb752a}
53
52
  .mg-page-header__decoration div:nth-child(4){background:#00afae}
54
- .mg-error-page__container{background:#fff;padding:2rem 1.5rem 1rem;max-width:900px;margin:2rem auto}
55
- .mg-button{display:inline-flex;align-items:center;justify-content:center;padding:1rem 1.5rem;font-size:1.4rem;font-weight:600;color:#fff;background:#004f91;border:1px solid #fff;cursor:pointer;text-decoration:none}
53
+ .mg-error-page__container{background:#fff;padding:20px 15px 10px;max-width:900px;margin:20px auto}
54
+ .mg-button{display:inline-flex;align-items:center;justify-content:center;padding:10px 15px;font-size:14px;font-weight:600;color:#fff;background:#004f91;border:1px solid #fff;cursor:pointer;text-decoration:none}
56
55
  .mg-button:hover{background:#3372a7;color:#fff;text-decoration:none}
57
56
  .mg-u-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(0);border:0}
58
57
  </style>
@@ -91,7 +90,7 @@
91
90
  document.head.appendChild(el);
92
91
  }
93
92
  // Load full CSS from CDN (includes fonts)
94
- load('link',{rel:'stylesheet',href:'https://assets.undrr.org/static/mangrove/1.3.3/css/style.css'});
93
+ load('link',{rel:'stylesheet',href:'https://assets.undrr.org/static/mangrove/1.4.1/css/style.css'});
95
94
  // Load analytics
96
95
  load('script',{src:'https://assets.undrr.org/static/analytics/v1.0.0/google_analytics_enhancements.js',crossorigin:'anonymous'});
97
96
  load('script',{src:'https://messaging.undrr.org/src/undrr-messaging.js',defer:''});
package/js/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # Vanilla JS scripts
2
+
3
+ Standalone JavaScript modules that work without React. Each file in this directory is auto-discovered by `webpack.entries.js` and produces a UMD bundle at `dist/js/{name}.min.js`.
4
+
5
+ ## How they ship
6
+
7
+ ```
8
+ stories/assets/js/my-script.js ← source (this directory)
9
+ ↓ webpack build (Config 1, UMD)
10
+ dist/js/my-script.min.js ← minified bundle (Drupal/CDN)
11
+ ↓ CopyPlugin
12
+ dist/assets/js/my-script.js ← unminified copy
13
+ ↓ npm-publish.yml (CI)
14
+ @undrr/undrr-mangrove/js/my-script.js ← npm package
15
+ ```
16
+
17
+ The GitHub Actions publish workflow (`npm-publish.yml`) copies `dist/assets/js/*` into the npm package at the top-level `js/` directory. This happens automatically — no manual registration is needed.
18
+
19
+ ## Current scripts
20
+
21
+ | File | Component | Purpose |
22
+ |------|-----------|---------|
23
+ | `on-this-page-nav.js` | [OnThisPageNav](../../Components/OnThisPageNav/) | Sticky horizontal "On this page" navigation with scroll-spy |
24
+ | `tabs.js` | [Tab](../../Components/Tab/) | Tabbed content with keyboard navigation and deep linking |
25
+ | `show-more.js` | [ShowMore](../../../Utilities/ShowMore/) | Expand/collapse toggle for content sections |
26
+ | `undrr.js` | — | Shared constants (key codes, breakpoints) and `window.UNDRR` namespace |
27
+
28
+ ## Adding a new script
29
+
30
+ 1. Create your file here (e.g., `my-feature.js`)
31
+ 2. Export a named init function: `export function mgMyFeature() { ... }`
32
+ 3. Auto-init at the bottom:
33
+ ```js
34
+ if (document.readyState === 'loading') {
35
+ document.addEventListener('DOMContentLoaded', mgMyFeature, false);
36
+ } else {
37
+ mgMyFeature();
38
+ }
39
+ ```
40
+ 4. Use `data-mg-*` attributes for activation and configuration
41
+ 5. Guard against double-init: `if (el.dataset.mgMyFeatureInitialized) return;`
42
+ 6. Create a companion SCSS file in `stories/Components/YourComponent/` and import it in `_components.scss`
43
+ 7. Run `yarn build` — the script appears at `dist/js/my-feature.min.js`
44
+
45
+ No webpack config or `src/index.js` changes are needed. The file is picked up automatically by the glob pattern in `webpack.entries.js`.
46
+
47
+ ## Differences from React components
48
+
49
+ | | Vanilla JS (this directory) | React components |
50
+ |---|---|---|
51
+ | Entry discovery | Auto (glob) | Manual (webpack.config.js) |
52
+ | Output | `dist/js/*.min.js` (UMD) | `dist/components/*.js` (ESM) |
53
+ | npm location | `@undrr/undrr-mangrove/js/` | `@undrr/undrr-mangrove/components/` |
54
+ | React dependency | None | Externalized (provided by import map) |
55
+ | Drupal integration | Load script directly as a library | Wrapper + hydration via `createHydrator` |
56
+
57
+ See [ARCHITECTURE.md](../../../docs/ARCHITECTURE.md) for the full build system documentation.
@@ -0,0 +1,76 @@
1
+ import { mgShowMore } from '../show-more';
2
+
3
+ afterEach(() => {
4
+ document.body.innerHTML = '';
5
+ });
6
+
7
+ function setupButton({ manual = false, targetSelector = null } = {}) {
8
+ document.body.innerHTML = `
9
+ <button
10
+ data-mg-show-more
11
+ ${manual ? 'data-mg-show-more-skip-auto-init' : ''}
12
+ ${targetSelector ? `data-mg-show-more-target="${targetSelector}"` : ''}
13
+ >Show more</button>
14
+ <div class="mg-show-more--container mg-show-more--collapsed"></div>
15
+ `;
16
+ return document.querySelector('[data-mg-show-more]');
17
+ }
18
+
19
+ describe('mgShowMore', () => {
20
+ describe('data-mg-show-more-skip-auto-init', () => {
21
+ it('skips elements with the attribute during auto-init (no scope)', () => {
22
+ const btn = setupButton({ manual: true });
23
+ mgShowMore(); // no scope = auto-init path
24
+ expect(btn.dataset.mgShowMoreInitialized).toBeUndefined();
25
+ });
26
+
27
+ it('processes elements with the attribute when scope is passed explicitly', () => {
28
+ const btn = setupButton({ manual: true });
29
+ mgShowMore([btn]);
30
+ expect(btn.dataset.mgShowMoreInitialized).toBe('true');
31
+ });
32
+ });
33
+
34
+ describe('idempotency', () => {
35
+ it('does not double-initialize when called twice', () => {
36
+ const btn = setupButton();
37
+ mgShowMore();
38
+ // After first call, flag is set and button has been clicked once (collapsed → expanded)
39
+ const target = document.querySelector('.mg-show-more--container');
40
+ const classAfterFirst = target.classList.contains('mg-show-more--collapsed');
41
+
42
+ mgShowMore();
43
+ // Second call should be a no-op — collapsed state must not change
44
+ expect(target.classList.contains('mg-show-more--collapsed')).toBe(classAfterFirst);
45
+ expect(btn.dataset.mgShowMoreInitialized).toBe('true');
46
+ });
47
+ });
48
+
49
+ describe('missing target guard', () => {
50
+ it('does not throw and warns when target element is missing', () => {
51
+ document.body.innerHTML = `
52
+ <button data-mg-show-more data-mg-show-more-target=".nonexistent">Show more</button>
53
+ `;
54
+ const warn = jest.spyOn(console, 'warn').mockImplementation(() => {});
55
+ expect(() => mgShowMore()).not.toThrow();
56
+ expect(warn).toHaveBeenCalledWith(
57
+ expect.stringContaining('.nonexistent')
58
+ );
59
+ warn.mockRestore();
60
+ });
61
+ });
62
+
63
+ describe('single-element scope', () => {
64
+ it('accepts a single HTMLElement without throwing', () => {
65
+ const btn = setupButton();
66
+ expect(() => mgShowMore(btn)).not.toThrow();
67
+ expect(btn.dataset.mgShowMoreInitialized).toBe('true');
68
+ });
69
+
70
+ it('does not apply skip-auto-init check when a single element is passed as scope', () => {
71
+ const btn = setupButton({ manual: true });
72
+ mgShowMore(btn); // single element, not an array
73
+ expect(btn.dataset.mgShowMoreInitialized).toBe('true');
74
+ });
75
+ });
76
+ });
@@ -0,0 +1,198 @@
1
+ import { mgTableOfContents, mgTableOfContentsInit } from '../table-of-contents';
2
+
3
+ afterEach(() => {
4
+ document.body.innerHTML = '';
5
+ });
6
+
7
+ function setupPage({ contentSelector, title, showTitle, skipAutoInit } = {}) {
8
+ document.body.innerHTML = `
9
+ <article class="mg-content">
10
+ <section
11
+ data-mg-table-of-contents
12
+ ${contentSelector ? `data-mg-table-of-contents-content="${contentSelector}"` : ''}
13
+ ${title ? `data-mg-table-of-contents-title="${title}"` : ''}
14
+ ${showTitle === false ? 'data-mg-table-of-contents-show-title="false"' : ''}
15
+ ${skipAutoInit ? 'data-mg-table-of-contents-skip-auto-init' : ''}
16
+ class="mg-table-of-contents"
17
+ ></section>
18
+ <h2 id="section-a">Section A</h2>
19
+ <p>Content A</p>
20
+ <h2>Section B</h2>
21
+ <p>Content B</p>
22
+ <h2 class="mg-table-of-contents--exclude">Excluded</h2>
23
+ <p>Should not appear</p>
24
+ <h2 class="mg-u-sr-only">Screen reader only</h2>
25
+ <p>Should not appear</p>
26
+ <h2>Section C</h2>
27
+ <p>Content C</p>
28
+ </article>
29
+ `;
30
+ return {
31
+ toc: document.querySelector('[data-mg-table-of-contents]'),
32
+ content: document.querySelector('.mg-content'),
33
+ };
34
+ }
35
+
36
+ describe('mgTableOfContents', () => {
37
+ it('generates TOC links from h2 headings', () => {
38
+ const { toc, content } = setupPage();
39
+ mgTableOfContents(content, toc);
40
+
41
+ const links = toc.querySelectorAll('li a');
42
+ expect(links).toHaveLength(3);
43
+ expect(links[0].textContent).toBe('Section A');
44
+ expect(links[1].textContent).toBe('Section B');
45
+ expect(links[2].textContent).toBe('Section C');
46
+ });
47
+
48
+ it('uses existing heading IDs', () => {
49
+ const { toc, content } = setupPage();
50
+ mgTableOfContents(content, toc);
51
+
52
+ const links = toc.querySelectorAll('li a');
53
+ expect(links[0].getAttribute('href')).toBe('#section-a');
54
+ });
55
+
56
+ it('generates IDs for headings without them', () => {
57
+ const { toc, content } = setupPage();
58
+ mgTableOfContents(content, toc);
59
+
60
+ // Index 2 because mgTableOfContents prepends an h2 title to the TOC
61
+ // element which is inside content: [0]=title, [1]=Section A, [2]=Section B
62
+ const sectionB = content.querySelectorAll('h2')[2];
63
+ expect(sectionB.id).toBe('section-b');
64
+
65
+ const links = toc.querySelectorAll('li a');
66
+ expect(links[1].getAttribute('href')).toBe('#section-b');
67
+ });
68
+
69
+ it('excludes headings with mg-table-of-contents--exclude', () => {
70
+ const { toc, content } = setupPage();
71
+ mgTableOfContents(content, toc);
72
+
73
+ const linkTexts = Array.from(toc.querySelectorAll('li a')).map(
74
+ a => a.textContent
75
+ );
76
+ expect(linkTexts).not.toContain('Excluded');
77
+ });
78
+
79
+ it('excludes headings with mg-u-sr-only', () => {
80
+ const { toc, content } = setupPage();
81
+ mgTableOfContents(content, toc);
82
+
83
+ const linkTexts = Array.from(toc.querySelectorAll('li a')).map(
84
+ a => a.textContent
85
+ );
86
+ expect(linkTexts).not.toContain('Screen reader only');
87
+ });
88
+
89
+ it('adds default title "On this page"', () => {
90
+ const { toc, content } = setupPage();
91
+ mgTableOfContents(content, toc);
92
+
93
+ const header = toc.querySelector('h2#on-this-page');
94
+ expect(header).not.toBeNull();
95
+ expect(header.textContent).toBe('On this page');
96
+ });
97
+
98
+ it('uses custom title from data attribute', () => {
99
+ const { toc, content } = setupPage({ title: 'Contents' });
100
+ mgTableOfContents(content, toc);
101
+
102
+ const header = toc.querySelector('h2#on-this-page');
103
+ expect(header.textContent).toBe('Contents');
104
+ });
105
+
106
+ it('hides title when show-title is false', () => {
107
+ const { toc, content } = setupPage({ showTitle: false });
108
+ mgTableOfContents(content, toc);
109
+
110
+ const header = toc.querySelector('h2#on-this-page');
111
+ expect(header).toBeNull();
112
+ });
113
+
114
+ it('adds mg-u-sr-only class when title is "hidden"', () => {
115
+ const { toc, content } = setupPage({ title: 'hidden' });
116
+ mgTableOfContents(content, toc);
117
+
118
+ const header = toc.querySelector('h2#on-this-page');
119
+ expect(header.classList.contains('mg-u-sr-only')).toBe(true);
120
+ });
121
+
122
+ it('renders ordered list when showNumbers is true', () => {
123
+ const { toc, content } = setupPage();
124
+ mgTableOfContents(content, toc, true);
125
+
126
+ expect(toc.querySelector('ol')).not.toBeNull();
127
+ expect(toc.querySelector('ul')).toBeNull();
128
+ });
129
+
130
+ it('logs error when content element is missing', () => {
131
+ const error = jest.spyOn(console, 'error').mockImplementation(() => {});
132
+ const { toc } = setupPage();
133
+ mgTableOfContents(null, toc);
134
+ expect(error).toHaveBeenCalled();
135
+ error.mockRestore();
136
+ });
137
+ });
138
+
139
+ describe('mgTableOfContentsInit', () => {
140
+ it('auto-initializes elements with data-mg-table-of-contents', () => {
141
+ const { toc } = setupPage({ contentSelector: '.mg-content' });
142
+ mgTableOfContentsInit();
143
+
144
+ expect(toc.dataset.mgTableOfContentsInitialized).toBe('true');
145
+ expect(toc.querySelectorAll('li a').length).toBeGreaterThan(0);
146
+ });
147
+
148
+ it('skips elements with skip-auto-init during auto-init', () => {
149
+ const { toc } = setupPage({ skipAutoInit: true });
150
+ mgTableOfContentsInit(); // no scope = auto-init path
151
+ expect(toc.dataset.mgTableOfContentsInitialized).toBeUndefined();
152
+ });
153
+
154
+ it('processes skip-auto-init elements when scope is passed explicitly', () => {
155
+ const { toc } = setupPage({
156
+ skipAutoInit: true,
157
+ contentSelector: '.mg-content',
158
+ });
159
+ mgTableOfContentsInit([toc]);
160
+ expect(toc.dataset.mgTableOfContentsInitialized).toBe('true');
161
+ });
162
+
163
+ it('does not double-initialize', () => {
164
+ const { toc } = setupPage({ contentSelector: '.mg-content' });
165
+ mgTableOfContentsInit();
166
+ const linkCountFirst = toc.querySelectorAll('li a').length;
167
+
168
+ mgTableOfContentsInit();
169
+ const linkCountSecond = toc.querySelectorAll('li a').length;
170
+ expect(linkCountSecond).toBe(linkCountFirst);
171
+ });
172
+
173
+ it('accepts a single HTMLElement as scope', () => {
174
+ const { toc } = setupPage({ contentSelector: '.mg-content' });
175
+ expect(() => mgTableOfContentsInit(toc)).not.toThrow();
176
+ expect(toc.dataset.mgTableOfContentsInitialized).toBe('true');
177
+ });
178
+
179
+ it('defaults to document.body when no content selector is set', () => {
180
+ const { toc } = setupPage(); // no contentSelector
181
+ mgTableOfContentsInit();
182
+
183
+ // Should still find headings since they're children of document.body
184
+ expect(toc.querySelectorAll('li a').length).toBeGreaterThan(0);
185
+ });
186
+
187
+ it('warns when content selector points to nonexistent element', () => {
188
+ const { toc } = setupPage({ contentSelector: '.nonexistent' });
189
+ const warn = jest.spyOn(console, 'warn').mockImplementation(() => {});
190
+ mgTableOfContentsInit();
191
+ expect(warn).toHaveBeenCalledWith(
192
+ expect.stringContaining('.nonexistent')
193
+ );
194
+ // Should still mark as initialized (no retry without clearing flag)
195
+ expect(toc.dataset.mgTableOfContentsInitialized).toBe('true');
196
+ warn.mockRestore();
197
+ });
198
+ });