newspack-components 4.8.0 → 4.9.0-alpha.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 (103) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/DEVELOPMENT.md +18 -17
  3. package/dist/cjs/action-card/index.js +85 -18
  4. package/dist/cjs/action-card/index.test.js +192 -5
  5. package/dist/cjs/action-card/style.scss +3 -9
  6. package/dist/cjs/card-feature/index.js +28 -3
  7. package/dist/cjs/card-feature/index.test.js +100 -45
  8. package/dist/cjs/card-feature/style.scss +1 -5
  9. package/dist/cjs/debug-badge/index.js +44 -0
  10. package/dist/cjs/debug-badge/index.test.js +50 -0
  11. package/dist/cjs/debug-badge/style.scss +26 -0
  12. package/dist/cjs/empty-state/index.test.js +37 -24
  13. package/dist/cjs/empty-state/root.js +2 -1
  14. package/dist/cjs/footer/index.js +52 -16
  15. package/dist/cjs/footer/index.test.js +208 -0
  16. package/dist/cjs/footer/style.scss +17 -1
  17. package/dist/cjs/index.js +7 -0
  18. package/dist/cjs/integration-icon/index.js +4 -4
  19. package/dist/cjs/integration-icon/style.scss +18 -2
  20. package/dist/cjs/integration-icons/active-campaign.js +4 -7
  21. package/dist/cjs/integration-icons/beehiiv.js +19 -0
  22. package/dist/cjs/integration-icons/gravity-forms.js +19 -0
  23. package/dist/cjs/integration-icons/index.js +26 -6
  24. package/dist/cjs/integration-icons/salesforce.js +5 -3
  25. package/dist/cjs/notice/index.js +1 -4
  26. package/dist/cjs/notice/style.scss +0 -19
  27. package/dist/cjs/plugin-toggle/index.js +1 -1
  28. package/dist/cjs/with-wizard-screen/index.js +1 -3
  29. package/dist/cjs/with-wizard-screen/index.test.js +0 -3
  30. package/dist/cjs/wizard/breadcrumbs-select.js +17 -4
  31. package/dist/cjs/wizard/breadcrumbs-select.test.js +17 -0
  32. package/dist/cjs/wizard/index.js +50 -13
  33. package/dist/cjs/wizard/index.test.js +304 -5
  34. package/dist/cjs/wizard/store/index.js +2 -0
  35. package/dist/esm/action-card/index.js +86 -21
  36. package/dist/esm/action-card/index.test.js +192 -1
  37. package/dist/esm/action-card/style.scss +3 -9
  38. package/dist/esm/card-feature/index.js +29 -4
  39. package/dist/esm/card-feature/index.test.js +100 -45
  40. package/dist/esm/card-feature/style.scss +1 -5
  41. package/dist/esm/debug-badge/index.js +39 -0
  42. package/dist/esm/debug-badge/index.test.js +46 -0
  43. package/dist/esm/debug-badge/style.scss +26 -0
  44. package/dist/esm/empty-state/index.test.js +37 -24
  45. package/dist/esm/empty-state/root.js +2 -1
  46. package/dist/esm/footer/index.js +52 -17
  47. package/dist/esm/footer/index.test.js +210 -0
  48. package/dist/esm/footer/style.scss +17 -1
  49. package/dist/esm/index.js +1 -0
  50. package/dist/esm/integration-icon/index.js +5 -5
  51. package/dist/esm/integration-icon/style.scss +18 -2
  52. package/dist/esm/integration-icons/active-campaign.js +5 -8
  53. package/dist/esm/integration-icons/beehiiv.js +13 -0
  54. package/dist/esm/integration-icons/gravity-forms.js +13 -0
  55. package/dist/esm/integration-icons/index.js +15 -4
  56. package/dist/esm/integration-icons/salesforce.js +5 -3
  57. package/dist/esm/notice/index.js +2 -5
  58. package/dist/esm/notice/style.scss +0 -19
  59. package/dist/esm/plugin-toggle/index.js +1 -1
  60. package/dist/esm/with-wizard-screen/index.js +2 -4
  61. package/dist/esm/with-wizard-screen/index.test.js +0 -3
  62. package/dist/esm/wizard/breadcrumbs-select.js +16 -3
  63. package/dist/esm/wizard/breadcrumbs-select.test.js +18 -1
  64. package/dist/esm/wizard/index.js +53 -16
  65. package/dist/esm/wizard/index.test.js +304 -5
  66. package/dist/esm/wizard/store/index.js +2 -0
  67. package/package.json +1 -1
  68. package/src/action-card/action-card.d.ts +3 -2
  69. package/src/action-card/index.js +81 -8
  70. package/src/action-card/index.test.js +159 -0
  71. package/src/action-card/style.scss +3 -9
  72. package/src/card-feature/README.md +10 -11
  73. package/src/card-feature/index.test.js +45 -15
  74. package/src/card-feature/index.tsx +43 -24
  75. package/src/card-feature/style.scss +1 -5
  76. package/src/debug-badge/index.js +36 -0
  77. package/src/debug-badge/index.test.js +39 -0
  78. package/src/debug-badge/style.scss +26 -0
  79. package/src/empty-state/README.md +11 -10
  80. package/src/empty-state/index.test.js +10 -0
  81. package/src/empty-state/root.tsx +2 -1
  82. package/src/empty-state/types.ts +1 -1
  83. package/src/footer/index.js +47 -2
  84. package/src/footer/index.test.js +114 -0
  85. package/src/footer/style.scss +17 -1
  86. package/src/index.js +1 -0
  87. package/src/integration-icon/index.tsx +5 -5
  88. package/src/integration-icon/style.scss +18 -2
  89. package/src/integration-icons/active-campaign.js +1 -5
  90. package/src/integration-icons/beehiiv.js +10 -0
  91. package/src/integration-icons/gravity-forms.js +10 -0
  92. package/src/integration-icons/index.js +14 -3
  93. package/src/integration-icons/salesforce.js +3 -3
  94. package/src/notice/index.js +2 -17
  95. package/src/notice/style.scss +0 -19
  96. package/src/plugin-toggle/index.js +1 -1
  97. package/src/with-wizard-screen/index.js +2 -2
  98. package/src/with-wizard-screen/index.test.js +0 -2
  99. package/src/wizard/breadcrumbs-select.js +12 -4
  100. package/src/wizard/breadcrumbs-select.test.js +15 -1
  101. package/src/wizard/index.js +45 -14
  102. package/src/wizard/index.test.js +169 -3
  103. package/src/wizard/store/index.js +2 -0
@@ -13,15 +13,28 @@ import { matchesRoute } from '../route-match';
13
13
  * @return {Array} Breadcrumb items `{ label, url? }`.
14
14
  */
15
15
  export var activeBreadcrumbs = function activeBreadcrumbs() {
16
+ var _activeSection;
17
+ var sections = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
18
+ var pathname = arguments.length > 1 ? arguments[1] : undefined;
19
+ return ((_activeSection = activeSection(sections, pathname)) === null || _activeSection === void 0 ? void 0 : _activeSection.breadcrumbs) || [];
20
+ };
21
+
22
+ /**
23
+ * Select the section matching the current route, falling back to the first.
24
+ *
25
+ * @param {Array} sections Wizard sections.
26
+ * @param {string} pathname Current router pathname.
27
+ * @return {Object|undefined} The active section, if any.
28
+ */
29
+ export var activeSection = function activeSection() {
16
30
  var sections = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
17
31
  var pathname = arguments.length > 1 ? arguments[1] : undefined;
18
32
  if (!(sections !== null && sections !== void 0 && sections.length)) {
19
- return [];
33
+ return undefined;
20
34
  }
21
- var match = sections.find(function (section) {
35
+ return sections.find(function (section) {
22
36
  return matchesRoute(section, pathname);
23
37
  }) || sections[0];
24
- return match.breadcrumbs || [];
25
38
  };
26
39
 
27
40
  /**
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Internal dependencies.
3
3
  */
4
- import { activeBreadcrumbs, appendSectionName } from './breadcrumbs-select';
4
+ import { activeBreadcrumbs, activeSection, appendSectionName } from './breadcrumbs-select';
5
5
  var SECTIONS = [{
6
6
  path: '/',
7
7
  breadcrumbs: [{
@@ -194,4 +194,21 @@ describe('appendSectionName', function () {
194
194
  label: 'Access Control'
195
195
  }]);
196
196
  });
197
+ });
198
+ describe('activeSection', function () {
199
+ var sections = [{
200
+ path: '/settings',
201
+ exact: true,
202
+ subHeaderText: 'Intro'
203
+ }, {
204
+ path: '/settings/:integrationId'
205
+ }];
206
+ it('returns the section matching the route', function () {
207
+ expect(activeSection(sections, '/settings/esp')).toBe(sections[1]);
208
+ expect(activeSection(sections, '/settings').subHeaderText).toBe('Intro');
209
+ });
210
+ it('falls back to the first section, or nothing without sections', function () {
211
+ expect(activeSection(sections, '/unknown')).toBe(sections[0]);
212
+ expect(activeSection([], '/settings')).toBeUndefined();
213
+ });
197
214
  });
@@ -2,6 +2,9 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
3
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
4
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
6
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
7
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
5
8
  /**
6
9
  * External dependencies.
7
10
  */
@@ -10,20 +13,19 @@ import classnames from 'classnames';
10
13
  /**
11
14
  * WordPress dependencies.
12
15
  */
13
- // Notice is aliased: `Notice` below is Newspack's own, which this file also uses.
14
- import { DropdownMenu, MenuGroup, MenuItem, Notice as CoreNotice, SlotFillProvider, createSlotFill,
16
+ import { DropdownMenu, MenuGroup, MenuItem, Notice, SlotFillProvider, createSlotFill,
15
17
  // eslint-disable-next-line @wordpress/no-unsafe-wp-apis
16
18
  __experimentalVStack as VStack } from '@wordpress/components';
17
19
  import { useDispatch, useSelect } from '@wordpress/data';
18
- import { cloneElement, createInterpolateElement, isValidElement, useEffect, useRef, useState, forwardRef } from '@wordpress/element';
20
+ import { cloneElement, createInterpolateElement, isValidElement, useLayoutEffect, useRef, useState, forwardRef } from '@wordpress/element';
19
21
  import { __ } from '@wordpress/i18n';
20
22
  import { category, chevronLeft, moreVertical } from '@wordpress/icons';
21
23
 
22
24
  /**
23
25
  * Internal dependencies
24
26
  */
25
- import { Footer, Notice, Button, TabbedNavigation, PluginInstaller, SectionHeader, HandoffMessage, Page, Waiting } from '../';
26
- import { activeBreadcrumbs, appendSectionName } from './breadcrumbs-select';
27
+ import { Footer, DebugBadge, Button, TabbedNavigation, PluginInstaller, SectionHeader, HandoffMessage, Page, Waiting } from '../';
28
+ import { activeBreadcrumbs, activeSection, appendSectionName } from './breadcrumbs-select';
27
29
  import Router from '../proxied-imports/router';
28
30
  import registerStore, { WIZARD_STORE_NAMESPACE } from './store';
29
31
  import WizardSnackbar from './components/WizardSnackbar';
@@ -60,6 +62,7 @@ var HashRouter = Router.HashRouter,
60
62
  Redirect = Router.Redirect,
61
63
  Route = Router.Route,
62
64
  Switch = Router.Switch,
65
+ matchPath = Router.matchPath,
63
66
  useLocation = Router.useLocation;
64
67
 
65
68
  /**
@@ -97,7 +100,10 @@ var ResetHeaderData = function ResetHeaderData() {
97
100
  var location = useLocation();
98
101
  var _useDispatch = useDispatch(WIZARD_STORE_NAMESPACE),
99
102
  resetHeaderData = _useDispatch.resetHeaderData;
100
- useEffect(function () {
103
+
104
+ // Must stay before paint: a passive effect here would run after a section that
105
+ // publishes from a layout effect, wiping the header it just set.
106
+ useLayoutEffect(function () {
101
107
  resetHeaderData();
102
108
  window.scrollTo(0, 0);
103
109
  }, [location.pathname, resetHeaderData]);
@@ -109,16 +115,46 @@ var ResetHeaderData = function ResetHeaderData() {
109
115
  * can read the current route and derive the active-tab breadcrumb.
110
116
  */
111
117
  var WizardHeaderRegion = function WizardHeaderRegion(_ref) {
118
+ var _activeSection;
112
119
  var hideHeader = _ref.hideHeader,
113
120
  headerText = _ref.headerText,
114
121
  sections = _ref.sections,
115
122
  sectionName = _ref.sectionName,
116
123
  subTitle = _ref.subTitle,
117
124
  actions = _ref.actions,
118
- tabbedNavigation = _ref.tabbedNavigation,
125
+ wizardTabbedNavigation = _ref.tabbedNavigation,
119
126
  children = _ref.children;
120
127
  var _useLocation = useLocation(),
121
128
  pathname = _useLocation.pathname;
129
+
130
+ // A section can carry its own tabs, built from its route params, in place of
131
+ // the wizard's. The first match wins, as it does in the wizard's `<Switch>`.
132
+ var tabbedNavigation = wizardTabbedNavigation;
133
+ var _iterator = _createForOfIteratorHelper(sections),
134
+ _step;
135
+ try {
136
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
137
+ var _section$exact;
138
+ var section = _step.value;
139
+ var match = matchPath(pathname, {
140
+ path: section.path,
141
+ exact: (_section$exact = section.exact) !== null && _section$exact !== void 0 ? _section$exact : false
142
+ });
143
+ if (match) {
144
+ if (typeof section.tabbedNavigation === 'function') {
145
+ tabbedNavigation = /*#__PURE__*/_jsx(TabbedNavigation, {
146
+ items: section.tabbedNavigation(match.params),
147
+ children: /*#__PURE__*/_jsx(WizardError, {})
148
+ });
149
+ }
150
+ break;
151
+ }
152
+ }
153
+ } catch (err) {
154
+ _iterator.e(err);
155
+ } finally {
156
+ _iterator.f();
157
+ }
122
158
  if (hideHeader) {
123
159
  // Without the Page shell the tabs still own the content: it renders
124
160
  // inside the active tab's panel.
@@ -138,9 +174,10 @@ var WizardHeaderRegion = function WizardHeaderRegion(_ref) {
138
174
  // Append any render-time leaf crumb(s) the section supplied via
139
175
  // headerData.sectionName (deduped against the current trailing label).
140
176
  breadcrumbItems = appendSectionName(breadcrumbItems, sectionName);
177
+ var sectionSubTitle = (_activeSection = activeSection(sections, pathname)) === null || _activeSection === void 0 ? void 0 : _activeSection.subHeaderText;
141
178
  return /*#__PURE__*/_jsx(Page, {
142
179
  breadcrumbItems: breadcrumbItems,
143
- subTitle: subTitle,
180
+ subTitle: sectionSubTitle !== null && sectionSubTitle !== void 0 ? sectionSubTitle : subTitle,
144
181
  actions: actions,
145
182
  tabbedNavigation: tabbedNavigation,
146
183
  children: children
@@ -153,7 +190,8 @@ var WizardHeaderRegion = function WizardHeaderRegion(_ref) {
153
190
  * @property {string} [subHeaderText] The sub-header text, optional.
154
191
  * @property {string} [apiSlug] The API slug, optional.
155
192
  * @property {string} [className] CSS classes, optional.
156
- * @property {any[]} sections Array of sections.
193
+ * @property {any[]} sections Array of sections. A section's own `subHeaderText` replaces the wizard's while it is active.
194
+ * Its optional `tabbedNavigation( params )` returns the tab items shown while its route matches.
157
195
  * @property {boolean} [hasSimpleFooter] Indicates if a simple footer is used, optional.
158
196
  * @property {() => void} [renderAboveSections] Function to render content above sections, optional.
159
197
  * @property {string[]} [requiredPlugins] Array of required plugin strings, optional.
@@ -203,6 +241,7 @@ var Wizard = function Wizard(_ref2, ref) {
203
241
  var actions = headerData.actions,
204
242
  backNav = headerData.backNav,
205
243
  badges = headerData.badges,
244
+ headerFullWidth = headerData.fullWidth,
206
245
  sectionDescription = headerData.sectionDescription,
207
246
  sectionMenu = headerData.sectionMenu,
208
247
  sectionName = headerData.sectionName,
@@ -307,7 +346,7 @@ var Wizard = function Wizard(_ref2, ref) {
307
346
  // describes the state of the whole site, not of the section below it, so it reads
308
347
  // as page chrome rather than as content.
309
348
  var inertGating = (_window$newspack_aux_ = window.newspack_aux_data) === null || _window$newspack_aux_ === void 0 ? void 0 : _window$newspack_aux_.inert_gating;
310
- var inertGatingNotice = (inertGating === null || inertGating === void 0 ? void 0 : inertGating.show) && /*#__PURE__*/_jsx(CoreNotice, {
349
+ var inertGatingNotice = (inertGating === null || inertGating === void 0 ? void 0 : inertGating.show) && /*#__PURE__*/_jsx(Notice, {
311
350
  status: "warning",
312
351
  isDismissible: false,
313
352
  className: "newspack-wizard__inert-gating-notice",
@@ -330,16 +369,16 @@ var Wizard = function Wizard(_ref2, ref) {
330
369
  bubblesVirtually: true
331
370
  }), /*#__PURE__*/_jsxs(Switch, {
332
371
  children: [routedSections.map(function (section, index) {
333
- var _section$exact;
372
+ var _section$exact2;
334
373
  var SectionComponent = section.render;
335
374
  var sectionProps = section.props || {};
336
375
  return /*#__PURE__*/_jsx(Route, {
337
- exact: (_section$exact = section.exact) !== null && _section$exact !== void 0 ? _section$exact : false,
376
+ exact: (_section$exact2 = section.exact) !== null && _section$exact2 !== void 0 ? _section$exact2 : false,
338
377
  path: section.path,
339
378
  render: function render(routerProps) {
340
379
  return /*#__PURE__*/_jsxs("div", {
341
380
  className: classnames('newspack-wizard__content', className, {
342
- 'newspack-wizard__content--full-width': section.fullWidth
381
+ 'newspack-wizard__content--full-width': headerFullWidth !== null && headerFullWidth !== void 0 ? headerFullWidth : section.fullWidth
343
382
  }),
344
383
  children: ['function' === typeof renderAboveSections ? renderAboveSections() : null, (sectionTitle || section.title) && /*#__PURE__*/_jsx(SectionHeader, {
345
384
  className: "newspack-wizard__section-header",
@@ -420,9 +459,7 @@ var Wizard = function Wizard(_ref2, ref) {
420
459
  }),
421
460
  children: [/*#__PURE__*/_jsxs(HashRouter, {
422
461
  hashType: "slash",
423
- children: [newspack_aux_data.is_debug_mode && /*#__PURE__*/_jsx(Notice, {
424
- debugMode: true
425
- }), /*#__PURE__*/_jsx(WizardHeaderRegion, {
462
+ children: [/*#__PURE__*/_jsx(DebugBadge, {}), /*#__PURE__*/_jsx(WizardHeaderRegion, {
426
463
  hideHeader: hideHeader,
427
464
  headerText: headerText,
428
465
  sections: routedSections,
@@ -11,25 +11,26 @@ import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
11
11
  * WordPress dependencies.
12
12
  */
13
13
  import apiFetch from '@wordpress/api-fetch';
14
+ import { select, useDispatch } from '@wordpress/data';
15
+ import { useLayoutEffect, useState } from '@wordpress/element';
14
16
 
15
17
  /**
16
18
  * Internal dependencies.
17
19
  */
18
20
  import Wizard from './';
19
21
  import { useWizardData } from './store/utils';
22
+ import { WIZARD_STORE_NAMESPACE } from './store';
20
23
  import { jsx as _jsx } from "react/jsx-runtime";
21
24
  jest.mock('@wordpress/api-fetch', function () {
22
25
  return jest.fn();
23
26
  });
24
27
 
25
- // Both globals are localized onto every real wizard screen. The footer's
26
- // ExternalLink needs a string href, and the debug Notice reads aux data.
27
- window.newspack_aux_data = {
28
- is_debug_mode: false
29
- };
28
+ // Localized onto every real wizard screen; the footer's ExternalLink needs a
29
+ // string href.
30
30
  window.newspack_urls = {
31
31
  support: 'https://help.newspack.com/'
32
32
  };
33
+ window.scrollTo = jest.fn();
33
34
  var SETTINGS = {
34
35
  minimumDonation: '5'
35
36
  };
@@ -251,4 +252,302 @@ describe('Wizard', function () {
251
252
  }
252
253
  }, _callee4);
253
254
  })));
255
+ });
256
+ var NarrowingSection = function NarrowingSection() {
257
+ var _useDispatch = useDispatch(WIZARD_STORE_NAMESPACE),
258
+ setHeaderData = _useDispatch.setHeaderData;
259
+ useLayoutEffect(function () {
260
+ setHeaderData({
261
+ fullWidth: false
262
+ });
263
+ }, [setHeaderData]);
264
+ return /*#__PURE__*/_jsx("div", {
265
+ children: "Narrowing section"
266
+ });
267
+ };
268
+ var RecoveringSection = function RecoveringSection() {
269
+ var _useDispatch2 = useDispatch(WIZARD_STORE_NAMESPACE),
270
+ setHeaderData = _useDispatch2.setHeaderData;
271
+ var _useState = useState(true),
272
+ _useState2 = _slicedToArray(_useState, 2),
273
+ failed = _useState2[0],
274
+ setFailed = _useState2[1];
275
+ useLayoutEffect(function () {
276
+ setHeaderData({
277
+ fullWidth: failed ? false : undefined
278
+ });
279
+ }, [setHeaderData, failed]);
280
+ return /*#__PURE__*/_jsx("button", {
281
+ onClick: function onClick() {
282
+ return setFailed(false);
283
+ },
284
+ children: "Retry"
285
+ });
286
+ };
287
+
288
+ // Two sections, because that is what mounts ResetHeaderData: a single-section
289
+ // wizard never exercises the reset these overrides have to survive.
290
+ var widthSections = function widthSections(renderSection) {
291
+ return [{
292
+ label: 'List',
293
+ path: '/',
294
+ exact: true,
295
+ fullWidth: true,
296
+ render: renderSection
297
+ }, {
298
+ label: 'Other',
299
+ path: '/other',
300
+ render: function render() {
301
+ return /*#__PURE__*/_jsx("div", {
302
+ children: "Other section"
303
+ });
304
+ }
305
+ }];
306
+ };
307
+ describe('Wizard content width', function () {
308
+ beforeEach(function () {
309
+ apiFetch.mockReset();
310
+ });
311
+ it('renders full-width when the section declares it and nothing overrides it', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
312
+ var _render, container, _t5;
313
+ return _regenerator().w(function (_context5) {
314
+ while (1) switch (_context5.n) {
315
+ case 0:
316
+ _render = render(/*#__PURE__*/_jsx(Wizard, {
317
+ headerText: "Test wizard",
318
+ sections: widthSections(function () {
319
+ return /*#__PURE__*/_jsx("div", {
320
+ children: "List content"
321
+ });
322
+ })
323
+ })), container = _render.container;
324
+ _t5 = expect;
325
+ _context5.n = 1;
326
+ return screen.findByText('List content');
327
+ case 1:
328
+ _t5(_context5.v).toBeInTheDocument();
329
+ expect(container.querySelector('.newspack-wizard__content')).toHaveClass('newspack-wizard__content--full-width');
330
+ case 2:
331
+ return _context5.a(2);
332
+ }
333
+ }, _callee5);
334
+ })));
335
+ it('falls back to the standard width when a section overrides fullWidth before paint', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() {
336
+ var _render2, container, _t6;
337
+ return _regenerator().w(function (_context6) {
338
+ while (1) switch (_context6.n) {
339
+ case 0:
340
+ _render2 = render(/*#__PURE__*/_jsx(Wizard, {
341
+ headerText: "Test wizard",
342
+ sections: widthSections(function () {
343
+ return /*#__PURE__*/_jsx(NarrowingSection, {});
344
+ })
345
+ })), container = _render2.container;
346
+ _t6 = expect;
347
+ _context6.n = 1;
348
+ return screen.findByText('Narrowing section');
349
+ case 1:
350
+ _t6(_context6.v).toBeInTheDocument();
351
+ expect(container.querySelector('.newspack-wizard__content')).not.toHaveClass('newspack-wizard__content--full-width');
352
+ case 2:
353
+ return _context6.a(2);
354
+ }
355
+ }, _callee6);
356
+ })));
357
+ it('restores the declared width when the section clears its override', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7() {
358
+ var _render3, container, retry;
359
+ return _regenerator().w(function (_context7) {
360
+ while (1) switch (_context7.n) {
361
+ case 0:
362
+ _render3 = render(/*#__PURE__*/_jsx(Wizard, {
363
+ headerText: "Test wizard",
364
+ sections: widthSections(function () {
365
+ return /*#__PURE__*/_jsx(RecoveringSection, {});
366
+ })
367
+ })), container = _render3.container;
368
+ _context7.n = 1;
369
+ return screen.findByRole('button', {
370
+ name: 'Retry'
371
+ });
372
+ case 1:
373
+ retry = _context7.v;
374
+ expect(container.querySelector('.newspack-wizard__content')).not.toHaveClass('newspack-wizard__content--full-width');
375
+ fireEvent.click(retry);
376
+ _context7.n = 2;
377
+ return waitFor(function () {
378
+ return expect(container.querySelector('.newspack-wizard__content')).toHaveClass('newspack-wizard__content--full-width');
379
+ });
380
+ case 2:
381
+ return _context7.a(2);
382
+ }
383
+ }, _callee7);
384
+ })));
385
+ it('keeps a section header published from a layout effect through the mount reset', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
386
+ var _t7;
387
+ return _regenerator().w(function (_context8) {
388
+ while (1) switch (_context8.n) {
389
+ case 0:
390
+ render(/*#__PURE__*/_jsx(Wizard, {
391
+ headerText: "Test wizard",
392
+ sections: widthSections(function () {
393
+ return /*#__PURE__*/_jsx(NarrowingSection, {});
394
+ })
395
+ }));
396
+ _t7 = expect;
397
+ _context8.n = 1;
398
+ return screen.findByText('Narrowing section');
399
+ case 1:
400
+ _t7(_context8.v).toBeInTheDocument();
401
+ expect(select(WIZARD_STORE_NAMESPACE).getHeaderData().fullWidth).toBe(false);
402
+ case 2:
403
+ return _context8.a(2);
404
+ }
405
+ }, _callee8);
406
+ })));
407
+ });
408
+ describe('Wizard section tabs', function () {
409
+ var sections = [{
410
+ label: 'List',
411
+ path: '/',
412
+ exact: true,
413
+ render: function render() {
414
+ return /*#__PURE__*/_jsx("div", {
415
+ children: "List content"
416
+ });
417
+ }
418
+ }, {
419
+ path: '/items/:itemId/logs/:tab?',
420
+ isHidden: true,
421
+ render: function render() {
422
+ return /*#__PURE__*/_jsx("div", {
423
+ children: "Logs content"
424
+ });
425
+ },
426
+ tabbedNavigation: function tabbedNavigation(_ref10) {
427
+ var itemId = _ref10.itemId;
428
+ return [{
429
+ label: 'First Tab',
430
+ path: "/items/".concat(itemId, "/logs"),
431
+ exact: true
432
+ }, {
433
+ label: 'Second Tab',
434
+ path: "/items/".concat(itemId, "/logs/second"),
435
+ exact: true
436
+ }];
437
+ }
438
+ }];
439
+ beforeEach(function () {
440
+ apiFetch.mockReset();
441
+ });
442
+ afterEach(function () {
443
+ window.location.hash = '';
444
+ });
445
+ it('shows the tabs a section builds from its route params while it matches', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
446
+ var _t8;
447
+ return _regenerator().w(function (_context9) {
448
+ while (1) switch (_context9.n) {
449
+ case 0:
450
+ window.location.hash = '#/items/42/logs/second';
451
+ render(/*#__PURE__*/_jsx(Wizard, {
452
+ headerText: "Test wizard",
453
+ sections: sections
454
+ }));
455
+ _t8 = expect;
456
+ _context9.n = 1;
457
+ return screen.findByText('Logs content');
458
+ case 1:
459
+ _t8(_context9.v).toBeInTheDocument();
460
+ expect(screen.getByRole('tab', {
461
+ name: 'First Tab'
462
+ })).toHaveAttribute('href', '#/items/42/logs');
463
+ expect(screen.getByRole('tab', {
464
+ name: 'Second Tab'
465
+ })).toHaveAttribute('aria-selected', 'true');
466
+ case 2:
467
+ return _context9.a(2);
468
+ }
469
+ }, _callee9);
470
+ })));
471
+ it('leaves the tabs out on a route the section does not match', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
472
+ var _t9;
473
+ return _regenerator().w(function (_context0) {
474
+ while (1) switch (_context0.n) {
475
+ case 0:
476
+ window.location.hash = '#/';
477
+ render(/*#__PURE__*/_jsx(Wizard, {
478
+ headerText: "Test wizard",
479
+ sections: sections
480
+ }));
481
+ _t9 = expect;
482
+ _context0.n = 1;
483
+ return screen.findByText('List content');
484
+ case 1:
485
+ _t9(_context0.v).toBeInTheDocument();
486
+ expect(screen.queryByRole('tab')).toBeNull();
487
+ case 2:
488
+ return _context0.a(2);
489
+ }
490
+ }, _callee0);
491
+ })));
492
+ });
493
+ describe('Wizard section subtitle', function () {
494
+ beforeEach(function () {
495
+ apiFetch.mockReset();
496
+ apiFetch.mockResolvedValue({});
497
+ });
498
+ it("shows the active section's subHeaderText in the header, falling back to the wizard's", /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee1() {
499
+ var _render4, container, subtitle, _t0, _t1;
500
+ return _regenerator().w(function (_context1) {
501
+ while (1) switch (_context1.n) {
502
+ case 0:
503
+ window.location.hash = '#/list';
504
+ _render4 = render(/*#__PURE__*/_jsx(Wizard, {
505
+ headerText: "Test wizard",
506
+ subHeaderText: "Wizard intro",
507
+ sections: [{
508
+ label: 'List',
509
+ path: '/list',
510
+ exact: true,
511
+ subHeaderText: 'List intro',
512
+ render: function render() {
513
+ return /*#__PURE__*/_jsx("div", {
514
+ children: "List view"
515
+ });
516
+ }
517
+ }, {
518
+ label: 'Item',
519
+ path: '/list/:id',
520
+ isHidden: true,
521
+ render: function render() {
522
+ return /*#__PURE__*/_jsx("div", {
523
+ children: "Item view"
524
+ });
525
+ }
526
+ }]
527
+ })), container = _render4.container;
528
+ subtitle = function subtitle() {
529
+ var _container$querySelec;
530
+ return (_container$querySelec = container.querySelector('.newspack-page__header-subtitle')) === null || _container$querySelec === void 0 ? void 0 : _container$querySelec.textContent;
531
+ };
532
+ _t0 = expect;
533
+ _context1.n = 1;
534
+ return screen.findByText('List view');
535
+ case 1:
536
+ _t0(_context1.v).toBeInTheDocument();
537
+ expect(subtitle()).toBe('List intro');
538
+ act(function () {
539
+ window.location.hash = '#/list/esp';
540
+ window.dispatchEvent(new HashChangeEvent('hashchange'));
541
+ });
542
+ _t1 = expect;
543
+ _context1.n = 2;
544
+ return screen.findByText('Item view');
545
+ case 2:
546
+ _t1(_context1.v).toBeInTheDocument();
547
+ expect(subtitle()).toBe('Wizard intro');
548
+ case 3:
549
+ return _context1.a(2);
550
+ }
551
+ }, _callee1);
552
+ })));
254
553
  });
@@ -19,6 +19,8 @@ import { createReduxStore, register, dispatch, select } from '@wordpress/data';
19
19
  import { createAction } from './utils.js';
20
20
  export var WIZARD_STORE_NAMESPACE = 'newspack/wizards';
21
21
  var DEFAULT_STATE = {
22
+ // `fullWidth` is deliberately absent: a default here would narrow every
23
+ // full-width section on reset, since Wizard only falls back when it is unset.
22
24
  headerData: {
23
25
  actions: [],
24
26
  backNav: '',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newspack-components",
3
- "version": "4.8.0",
3
+ "version": "4.9.0-alpha.1",
4
4
  "description": "Newspack design system components",
5
5
  "author": "Automattic",
6
6
  "license": "GPL-2.0-or-later",
@@ -10,8 +10,9 @@ export interface ActionCardProps {
10
10
  badges?: CardBadge[];
11
11
  className?: string;
12
12
  indent?: string;
13
- notification?: string | Error | null;
14
- notificationLevel?: 'error' | 'warning' | 'info';
13
+ notification?: React.ReactNode | Error | null;
14
+ notificationLevel?: 'error' | 'warning' | 'info' | 'success';
15
+ notificationHTML?: boolean;
15
16
  isMedium?: boolean;
16
17
  disabled?: boolean | string;
17
18
  hasGreyHeader?: boolean;