@undrr/undrr-mangrove 2.0.0-rc.2 → 2.0.0

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 (124) hide show
  1. package/README.md +11 -6
  2. package/components/BookCard.js +1 -1
  3. package/components/Breadcrumbs.js +2 -2
  4. package/components/Checkbox.js +1 -1
  5. package/components/Chips.js +1 -1
  6. package/components/CopyButton.js +2 -2
  7. package/components/CtaButton.js +1 -1
  8. package/components/Drawer.js +1 -1
  9. package/components/EmbedContainer.js +1 -1
  10. package/components/Footer.js +1 -1
  11. package/components/FormAction.js +1 -1
  12. package/components/FormErrorSummary.js +1 -1
  13. package/components/FormGroup.js +1 -1
  14. package/components/FullWidth.js +1 -1
  15. package/components/Gallery.js +2 -2
  16. package/components/Hero.js +2 -2
  17. package/components/HighlightBox.js +1 -1
  18. package/components/HorizontalBookCard.js +1 -1
  19. package/components/HorizontalCard.js +1 -1
  20. package/components/IconCard.js +1 -1
  21. package/components/Legend.js +1 -1
  22. package/components/Loader.js +1 -1
  23. package/components/MegaMenu.js +2 -2
  24. package/components/Notice.js +2 -2
  25. package/components/PageHeader.js +2 -2
  26. package/components/Pager.js +2 -2
  27. package/components/QuoteHighlight.js +2 -2
  28. package/components/QuoteHighlight.js.LICENSE.txt +2 -0
  29. package/components/Radio.js +1 -1
  30. package/components/Range.js +1 -1
  31. package/components/ScrollContainer.js +2 -2
  32. package/components/SegmentedControl.js +9 -0
  33. package/components/SegmentedControl.js.LICENSE.txt +9 -0
  34. package/components/Select.js +1 -1
  35. package/components/ServiceNotice.js +2 -2
  36. package/components/ShareButtons.js +2 -2
  37. package/components/ShowMore.js +2 -2
  38. package/components/SkipLink.js +1 -1
  39. package/components/StatsCard.js +1 -1
  40. package/components/SyndicationSearchWidget.js +2 -2
  41. package/components/Tab.js +2 -2
  42. package/components/TextCta.js +1 -1
  43. package/components/TextInput.js +1 -1
  44. package/components/Textarea.js +1 -1
  45. package/components/Tree.js +1 -1
  46. package/components/UserFeedback.js +1 -1
  47. package/components/VerticalCard.js +1 -1
  48. package/components/hydrate.js +2 -2
  49. package/css/style-all.css +60 -40
  50. package/css/style-all.css.map +1 -1
  51. package/css/style-delta.css +59 -39
  52. package/css/style-delta.css.map +1 -1
  53. package/css/style-gutenberg.css +16 -7
  54. package/css/style-gutenberg.css.map +1 -1
  55. package/css/style-irp.css +60 -40
  56. package/css/style-irp.css.map +1 -1
  57. package/css/style-mcr.css +59 -39
  58. package/css/style-mcr.css.map +1 -1
  59. package/css/style-preventionweb.css +60 -40
  60. package/css/style-preventionweb.css.map +1 -1
  61. package/css/style.css +59 -39
  62. package/css/style.css.map +1 -1
  63. package/error-pages/401.html +3 -3
  64. package/error-pages/403.html +3 -3
  65. package/error-pages/404.html +4 -4
  66. package/error-pages/429.html +3 -3
  67. package/error-pages/500.html +6 -6
  68. package/error-pages/502.html +3 -3
  69. package/error-pages/503.html +3 -3
  70. package/error-pages/504.html +3 -3
  71. package/error-pages/5xx.html +4 -4
  72. package/error-pages/challenge.html +3 -3
  73. package/error-pages/managed-challenge.html +3 -3
  74. package/js/README.md +31 -25
  75. package/js/copy-button.js +17 -0
  76. package/js/drawer.js +752 -0
  77. package/js/hub-header.js +217 -0
  78. package/js/show-more.js +144 -10
  79. package/js/switch-pending.js +195 -46
  80. package/package.json +8 -3
  81. package/scss/Atom/BaseTypography/Blockquote/blockquote.scss +2 -1
  82. package/scss/Atom/BaseTypography/Cite/cite.scss +2 -1
  83. package/scss/Atom/BaseTypography/Code/code.scss +8 -2
  84. package/scss/Atom/Icons/icons.scss +1 -1
  85. package/scss/Atom/Images/AuthorImage/author-image.scss +16 -1
  86. package/scss/Atom/StatusLabel/status-label.scss +331 -8
  87. package/scss/Atom/Table/table.scss +8 -3
  88. package/scss/Atom/Tag/tag.scss +20 -2
  89. package/scss/Components/Breadcrumbs/breadcrumbs.scss +8 -4
  90. package/scss/Components/Buttons/CtaButton/cta-button.scss +15 -0
  91. package/scss/Components/Cards/Card/card.scss +37 -10
  92. package/scss/Components/Cards/StatsCard/stats-card.scss +2 -10
  93. package/scss/Components/DataViz/Legend/legend.scss +2 -1
  94. package/scss/Components/ErrorPages/error-pages.scss +8 -0
  95. package/scss/Components/Forms/SegmentedControl/segmented-control.scss +412 -0
  96. package/scss/Components/Forms/_form-base.scss +109 -18
  97. package/scss/Components/Hero/hero.scss +296 -57
  98. package/scss/Components/MegaMenu/mega-menu.scss +26 -6
  99. package/scss/Components/OnThisPageNav/on-this-page-nav.scss +3 -1
  100. package/scss/Components/PageHeader/page-header.scss +13 -28
  101. package/scss/Components/Pager/pager.scss +3 -1
  102. package/scss/Components/QuoteHighlight/quote-highlight.scss +6 -1
  103. package/scss/Components/ScrollContainer/scroll-container.scss +11 -0
  104. package/scss/Components/SyndicationSearchWidget/syndication-search-widget.scss +51 -24
  105. package/scss/Components/Tab/tab.scss +1 -1
  106. package/scss/Components/TextCta/text-cta.scss +11 -3
  107. package/scss/Patterns/HubHeader/hub-header.scss +14 -3
  108. package/scss/Utilities/FullWidth/full-width.scss +14 -1
  109. package/scss/assets/scss/_components.scss +1 -0
  110. package/scss/assets/scss/_fonts.scss +11 -0
  111. package/scss/assets/scss/_foundational.scss +7 -0
  112. package/scss/assets/scss/_mixins.scss +32 -0
  113. package/scss/assets/scss/_tokens-data-viz.scss +9 -0
  114. package/scss/assets/scss/_variables.scss +16 -3
  115. package/scss/assets/scss/generated/_tokens-irp.scss +20 -4
  116. package/scss/assets/scss/generated/_tokens-mangrove.scss +6 -0
  117. package/scss/assets/scss/generated/_tokens-preventionweb.scss +20 -4
  118. package/js/__tests__/copy-button.test.js +0 -112
  119. package/js/__tests__/show-more.test.js +0 -80
  120. package/js/__tests__/table-of-contents.test.js +0 -196
  121. package/scss/Components/SyndicationSearchWidget/SyndicationSearchWidget.scss +0 -11
  122. package/scss/Components/TableOfContents/TableOfContents.scss +0 -11
  123. package/scss/Utilities/FullWidth/FullWidth.scss +0 -11
  124. package/scss/Utilities/ShowMore/ShowMore.scss +0 -11
@@ -1,80 +0,0 @@
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(
41
- 'mg-show-more--collapsed'
42
- );
43
-
44
- mgShowMore();
45
- // Second call should be a no-op — collapsed state must not change
46
- expect(target.classList.contains('mg-show-more--collapsed')).toBe(
47
- classAfterFirst
48
- );
49
- expect(btn.dataset.mgShowMoreInitialized).toBe('true');
50
- });
51
- });
52
-
53
- describe('missing target guard', () => {
54
- it('does not throw and warns when target element is missing', () => {
55
- document.body.innerHTML = `
56
- <button data-mg-show-more data-mg-show-more-target=".nonexistent">Show more</button>
57
- `;
58
- const warn = jest.spyOn(console, 'warn').mockImplementation(() => {});
59
- expect(() => mgShowMore()).not.toThrow();
60
- expect(warn).toHaveBeenCalledWith(
61
- expect.stringContaining('.nonexistent')
62
- );
63
- warn.mockRestore();
64
- });
65
- });
66
-
67
- describe('single-element scope', () => {
68
- it('accepts a single HTMLElement without throwing', () => {
69
- const btn = setupButton();
70
- expect(() => mgShowMore(btn)).not.toThrow();
71
- expect(btn.dataset.mgShowMoreInitialized).toBe('true');
72
- });
73
-
74
- it('does not apply skip-auto-init check when a single element is passed as scope', () => {
75
- const btn = setupButton({ manual: true });
76
- mgShowMore(btn); // single element, not an array
77
- expect(btn.dataset.mgShowMoreInitialized).toBe('true');
78
- });
79
- });
80
- });
@@ -1,196 +0,0 @@
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(expect.stringContaining('.nonexistent'));
192
- // Should still mark as initialized (no retry without clearing flag)
193
- expect(toc.dataset.mgTableOfContentsInitialized).toBe('true');
194
- warn.mockRestore();
195
- });
196
- });
@@ -1,11 +0,0 @@
1
- /**
2
- * Syndication Search Widget
3
- *
4
- * Elasticsearch search interface with faceted filtering, multi-layout result cards, and live metrics.
5
- *
6
- * Full documentation:
7
- * https://github.com/unisdr/undrr-mangrove/blob/main/stories/Components/SyndicationSearchWidget/SyndicationSearchWidget.mdx
8
- */
9
- // SyndicationSearchWidget.scss: DEPRECATED, use syndication-search-widget.scss
10
- @import "./syndication-search-widget";
11
- @warn "SyndicationSearchWidget.scss is deprecated. Import syndication-search-widget.scss instead.";
@@ -1,11 +0,0 @@
1
- /**
2
- * Table of Contents (Alias)
3
- *
4
- * CamelCase alias forwarding to table-of-contents.scss.
5
- *
6
- * Full documentation:
7
- * https://github.com/unisdr/undrr-mangrove/blob/main/stories/Components/TableOfContents/TableOfContents.mdx
8
- */
9
- // TableOfContents.scss — DEPRECATED, use table-of-contents.scss
10
- @import "./table-of-contents";
11
- @warn "TableOfContents.scss is deprecated. Import table-of-contents.scss instead.";
@@ -1,11 +0,0 @@
1
- /**
2
- * Full Width (Alias)
3
- *
4
- * CamelCase alias forwarding to full-width.scss.
5
- *
6
- * Full documentation:
7
- * https://github.com/unisdr/undrr-mangrove/blob/main/stories/Utilities/FullWidth/FullWidth.mdx
8
- */
9
- // FullWidth.scss — DEPRECATED, use full-width.scss
10
- @import "./full-width";
11
- @warn "FullWidth.scss is deprecated. Import full-width.scss instead.";
@@ -1,11 +0,0 @@
1
- /**
2
- * Show More (Alias)
3
- *
4
- * CamelCase alias forwarding to show-more.scss.
5
- *
6
- * Full documentation:
7
- * https://github.com/unisdr/undrr-mangrove/blob/main/stories/Utilities/ShowMore/ShowMore.mdx
8
- */
9
- // ShowMore.scss — DEPRECATED, use show-more.scss
10
- @import "./show-more";
11
- @warn "ShowMore.scss is deprecated. Import show-more.scss instead.";