@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.
- package/README.md +33 -29
- package/components/BarChart.js +2 -2
- package/components/BookCard.js +6 -0
- package/components/BookCard.js.LICENSE.txt +11 -0
- package/components/Breadcrumbs.js +6 -0
- package/components/Breadcrumbs.js.LICENSE.txt +9 -0
- package/components/Checkbox.js +6 -0
- package/components/Checkbox.js.LICENSE.txt +9 -0
- package/components/Chips.js +6 -0
- package/components/Chips.js.LICENSE.txt +9 -0
- package/components/CtaButton.js +6 -0
- package/components/CtaButton.js.LICENSE.txt +9 -0
- package/components/EmbedContainer.js +6 -0
- package/components/EmbedContainer.js.LICENSE.txt +9 -0
- package/components/Fetcher.js +2 -2
- package/components/Footer.js +6 -0
- package/components/Footer.js.LICENSE.txt +9 -0
- package/components/FormErrorSummary.js +6 -0
- package/components/FormErrorSummary.js.LICENSE.txt +9 -0
- package/components/FormGroup.js +6 -0
- package/components/FormGroup.js.LICENSE.txt +9 -0
- package/components/FullWidth.js +6 -0
- package/components/FullWidth.js.LICENSE.txt +9 -0
- package/components/Gallery.js +1 -1
- package/components/Hero.js +6 -0
- package/components/Hero.js.LICENSE.txt +9 -0
- package/components/HighlightBox.js +6 -0
- package/components/HighlightBox.js.LICENSE.txt +9 -0
- package/components/HorizontalBookCard.js +6 -0
- package/components/HorizontalBookCard.js.LICENSE.txt +11 -0
- package/components/HorizontalCard.js +6 -0
- package/components/HorizontalCard.js.LICENSE.txt +11 -0
- package/components/IconCard.js +2 -2
- package/components/Loader.js +6 -0
- package/components/Loader.js.LICENSE.txt +9 -0
- package/components/MapComponent.js +2 -2
- package/components/MegaMenu.js +2 -2
- package/components/PageHeader.js +6 -0
- package/components/PageHeader.js.LICENSE.txt +9 -0
- package/components/Pager.js +2 -2
- package/components/QuoteHighlight.js +1 -1
- package/components/Radio.js +6 -0
- package/components/Radio.js.LICENSE.txt +9 -0
- package/components/ScrollContainer.js +1 -1
- package/components/Select.js +6 -0
- package/components/Select.js.LICENSE.txt +9 -0
- package/components/ShareButtons.js +1 -1
- package/components/ShowMore.js +6 -0
- package/components/ShowMore.js.LICENSE.txt +9 -0
- package/components/StatsCard.js +2 -2
- package/components/SyndicationSearchWidget.js +2 -2
- package/components/Tab.js +6 -0
- package/components/Tab.js.LICENSE.txt +9 -0
- package/components/TextCta.js +6 -0
- package/components/TextCta.js.LICENSE.txt +11 -0
- package/components/TextInput.js +6 -0
- package/components/TextInput.js.LICENSE.txt +9 -0
- package/components/Textarea.js +6 -0
- package/components/Textarea.js.LICENSE.txt +9 -0
- package/components/VerticalCard.js +6 -0
- package/components/VerticalCard.js.LICENSE.txt +11 -0
- package/components/hydrate.js +1 -1
- package/css/style-delta.css +467 -0
- package/css/style-delta.css.map +1 -0
- package/css/style-gutenberg.css +21 -18
- package/css/style-gutenberg.css.map +1 -1
- package/css/style-irp-legacy.css +472 -0
- package/css/style-irp-legacy.css.map +1 -0
- package/css/style-irp.css +99 -103
- package/css/style-irp.css.map +1 -1
- package/css/style-legacy.css +465 -0
- package/css/style-legacy.css.map +1 -0
- package/css/style-mcr-legacy.css +472 -0
- package/css/style-mcr-legacy.css.map +1 -0
- package/css/style-mcr.css +99 -103
- package/css/style-mcr.css.map +1 -1
- package/css/style-preventionweb-legacy.css +472 -0
- package/css/style-preventionweb-legacy.css.map +1 -0
- package/css/style-preventionweb.css +99 -103
- package/css/style-preventionweb.css.map +1 -1
- package/css/style.css +99 -103
- package/css/style.css.map +1 -1
- package/error-pages/401.html +10 -11
- package/error-pages/403.html +11 -12
- package/error-pages/404.html +13 -14
- package/error-pages/429.html +12 -13
- package/error-pages/500.html +10 -11
- package/error-pages/502.html +12 -13
- package/error-pages/503.html +12 -13
- package/error-pages/504.html +10 -11
- package/error-pages/5xx.html +10 -11
- package/error-pages/challenge.html +12 -13
- package/error-pages/managed-challenge.html +12 -13
- package/js/README.md +57 -0
- package/js/__tests__/show-more.test.js +76 -0
- package/js/__tests__/table-of-contents.test.js +198 -0
- package/js/on-this-page-nav.js +531 -0
- package/js/show-more.js +24 -3
- package/js/table-of-contents.js +168 -0
- package/js/tabs.js +445 -83
- package/package.json +1 -1
- package/scss/Atom/BaseTypography/Blockquote/blockquote.scss +1 -8
- package/scss/Atom/BaseTypography/Cite/cite.scss +2 -2
- package/scss/Atom/Images/AuthorImage/author-image.scss +4 -4
- package/scss/Atom/Images/ImageCaptionCredit/image-caption-credit.scss +24 -28
- package/scss/Atom/Images/ImageCredit/image-credit.scss +1 -1
- package/scss/Atom/Layout/Container/container.scss +2 -2
- package/scss/Atom/Layout/Grid/grid.scss +1 -1
- package/scss/Atom/ReachElement/Details/details.scss +6 -6
- package/scss/Atom/ReachElement/Figcaption/figcaption.scss +1 -1
- package/scss/Atom/Table/table.scss +0 -8
- package/scss/Components/Boilerplate/boilerplate.scss +2 -2
- package/scss/Components/Breadcrumbs/breadcrumbs.scss +2 -9
- package/scss/Components/Buttons/Chips/chips.scss +5 -19
- package/scss/Components/Buttons/CtaButton/buttons.scss +3 -124
- package/scss/Components/Buttons/CtaButton/cta-button.scss +99 -0
- package/scss/Components/Buttons/ShareButtons/share-buttons.scss +2 -2
- package/scss/Components/Cards/Card/card.scss +45 -6
- package/scss/Components/Cards/StatsCard/stats-card.scss +2 -2
- package/scss/Components/ErrorPages/error-pages.scss +12 -12
- package/scss/Components/Footer/footer.scss +72 -4
- package/scss/Components/Forms/Select/select.scss +2 -2
- package/scss/Components/Forms/_form-base.scss +5 -5
- package/scss/Components/Forms/_form-legacy.scss +1 -1
- package/scss/Components/Gallery/gallery.scss +7 -7
- package/scss/Components/Hero/hero.scss +43 -23
- package/scss/Components/HighlightBox/highlight-box.scss +5 -5
- package/scss/Components/MegaMenu/mega-menu.scss +750 -0
- package/scss/Components/MegaMenu/megamenu.scss +3 -733
- package/scss/Components/OnThisPageNav/on-this-page-nav.scss +206 -0
- package/scss/Components/PageHeader/page-header.scss +5 -5
- package/scss/Components/Pagination/pagination.scss +2 -2
- package/scss/Components/ScrollContainer/scroll-container.scss +1 -1
- package/scss/Components/Snackbar/snackbar.scss +1 -1
- package/scss/Components/SyndicationSearchWidget/SyndicationSearchWidget.scss +3 -1480
- package/scss/Components/SyndicationSearchWidget/syndication-search-widget.scss +1515 -0
- package/scss/Components/Tab/tab.scss +68 -9
- package/scss/Components/TableOfContents/TableOfContents.scss +3 -17
- package/scss/Components/TableOfContents/table-of-contents.scss +17 -0
- package/scss/Components/TextCta/text-cta.scss +139 -0
- package/scss/Components/TextCta/textcta.scss +3 -27
- package/scss/Molecules/ImageCaption/image-caption.scss +6 -16
- package/scss/Molecules/SectionHeader/section-header.scss +8 -0
- package/scss/Molecules/SectionHeader/sectionheader.scss +3 -8
- package/scss/Utilities/FullWidth/FullWidth.scss +3 -23
- package/scss/Utilities/FullWidth/full-width.scss +23 -0
- package/scss/Utilities/Loader/loader.scss +1 -1
- package/scss/Utilities/ShowMore/ShowMore.scss +3 -26
- package/scss/Utilities/ShowMore/show-more.scss +26 -0
- package/scss/assets/scss/_components.scss +15 -9
- package/scss/assets/scss/_foundational.scss +13 -7
- package/scss/assets/scss/_mixins.scss +9 -314
- package/scss/assets/scss/_utility.scss +19 -71
- package/scss/assets/scss/_variables-delta.scss +110 -0
- package/scss/assets/scss/_variables-irp.scss +1 -1
- package/scss/assets/scss/_variables-mcr.scss +1 -1
- package/scss/assets/scss/_variables-preventionweb.scss +1 -1
- package/scss/assets/scss/_variables.scss +101 -34
- package/scss/assets/scss/style-delta.scss +8 -0
- package/scss/assets/scss/style-gutenberg.scss +2 -2
- package/scss/assets/scss/style-irp-legacy.scss +20 -0
- package/scss/assets/scss/style-legacy.scss +20 -0
- package/scss/assets/scss/style-mcr-legacy.scss +20 -0
- package/scss/assets/scss/style-preventionweb-legacy.scss +20 -0
- package/scss/Components/BlockquoteComponent/blockquotecomp.scss +0 -31
- package/scss/Components/Buttons/CtaLink/cta-link.scss +0 -61
package/error-pages/504.html
CHANGED
|
@@ -26,25 +26,24 @@
|
|
|
26
26
|
<style>
|
|
27
27
|
/* Critical inline styles for immediate render */
|
|
28
28
|
*,*::before,*::after{box-sizing:border-box}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
h1
|
|
32
|
-
|
|
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
|
|
38
|
-
.mg-error-page{min-height:100vh;background:#004f91;padding-bottom:
|
|
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:
|
|
46
|
-
.mg-error-page__actions{display:flex;gap:
|
|
47
|
-
.mg-button{display:inline-flex;align-items:center;justify-content:center;padding:
|
|
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.
|
|
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:''});
|
package/error-pages/5xx.html
CHANGED
|
@@ -29,25 +29,24 @@
|
|
|
29
29
|
<style>
|
|
30
30
|
/* Critical inline styles for immediate render */
|
|
31
31
|
*,*::before,*::after{box-sizing:border-box}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
h1
|
|
35
|
-
|
|
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
|
|
41
|
-
.mg-error-page{min-height:100vh;background:#004f91;padding-bottom:
|
|
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:
|
|
49
|
-
.mg-error-page__actions{display:flex;gap:
|
|
50
|
-
.mg-button{display:inline-flex;align-items:center;justify-content:center;padding:
|
|
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.
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
h1
|
|
35
|
-
|
|
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
|
|
41
|
-
.mg-error-page{min-height:100vh;background:#004f91;padding-bottom:
|
|
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:
|
|
44
|
-
.mg-error-page--challenge .undrr-logo{width:100%;margin:0 0
|
|
45
|
-
.mg-error-page--challenge h1{font-size:
|
|
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:
|
|
53
|
-
.mg-button{display:inline-flex;align-items:center;justify-content:center;padding:
|
|
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.
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
h1
|
|
37
|
-
|
|
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
|
|
43
|
-
.mg-error-page{min-height:100vh;background:#004f91;padding-bottom:
|
|
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:
|
|
46
|
-
.mg-error-page--challenge .undrr-logo{width:100%;margin:0 0
|
|
47
|
-
.mg-error-page--challenge h1{font-size:
|
|
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:
|
|
55
|
-
.mg-button{display:inline-flex;align-items:center;justify-content:center;padding:
|
|
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.
|
|
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
|
+
});
|