newspack-components 4.6.2 → 4.7.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 (148) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/colors/colors.module.scss +13 -7
  3. package/dist/cjs/autocomplete-tokenfield/style.scss +4 -2
  4. package/dist/cjs/breadcrumbs/format-count.js +33 -0
  5. package/dist/cjs/breadcrumbs/index.js +78 -20
  6. package/dist/cjs/breadcrumbs/index.test.js +110 -0
  7. package/dist/cjs/breadcrumbs/style.scss +6 -0
  8. package/dist/cjs/button/index.js +41 -17
  9. package/dist/cjs/button/index.test.js +273 -0
  10. package/dist/cjs/card/core-card.js +9 -4
  11. package/dist/cjs/card/core-card.test.js +29 -0
  12. package/dist/cjs/card/index.js +4 -2
  13. package/dist/cjs/card/style-core.scss +59 -0
  14. package/dist/cjs/card-feature/index.js +7 -1
  15. package/dist/cjs/card-feature/index.test.js +92 -0
  16. package/dist/cjs/color-picker/index.js +1 -1
  17. package/dist/cjs/confirm-dialog/index.js +26 -9
  18. package/dist/cjs/confirm-dialog/index.test.js +176 -0
  19. package/dist/cjs/dataviews/index.js +11 -2
  20. package/dist/cjs/divider/index.js +13 -0
  21. package/dist/cjs/drawer/action.js +74 -0
  22. package/dist/cjs/drawer/body.js +79 -0
  23. package/dist/cjs/drawer/close-icon.js +45 -0
  24. package/dist/cjs/drawer/content.js +72 -0
  25. package/dist/cjs/drawer/context.js +23 -0
  26. package/dist/cjs/drawer/context.test.js +43 -0
  27. package/dist/cjs/drawer/divider.js +24 -0
  28. package/dist/cjs/drawer/footer.js +26 -0
  29. package/dist/cjs/drawer/header.js +26 -0
  30. package/dist/cjs/drawer/index.js +42 -0
  31. package/dist/cjs/drawer/index.test.js +1287 -0
  32. package/dist/cjs/drawer/root.js +350 -0
  33. package/dist/cjs/drawer/slots.test.js +422 -0
  34. package/dist/cjs/drawer/style.scss +205 -0
  35. package/dist/cjs/drawer/title.js +54 -0
  36. package/dist/cjs/drawer/types.js +5 -0
  37. package/dist/cjs/drawer/use-exit-animation.js +59 -0
  38. package/dist/cjs/hooks/use-confirm-dialog.js +15 -3
  39. package/dist/cjs/hooks/use-unsaved-changes-dialog.js +19 -4
  40. package/dist/cjs/index.js +28 -1
  41. package/dist/cjs/page/index.js +1 -1
  42. package/dist/cjs/page-control/index.js +82 -0
  43. package/dist/cjs/plugin-settings/SettingsSection.js +7 -1
  44. package/dist/cjs/section-header/index.js +5 -2
  45. package/dist/cjs/section-header/style.scss +25 -12
  46. package/dist/cjs/table-card/index.js +67 -0
  47. package/dist/cjs/table-card/index.test.js +153 -0
  48. package/dist/cjs/with-wizard-screen/style.scss +8 -0
  49. package/dist/cjs/wizard/index.js +102 -38
  50. package/dist/esm/autocomplete-tokenfield/style.scss +4 -2
  51. package/dist/esm/breadcrumbs/format-count.js +27 -0
  52. package/dist/esm/breadcrumbs/index.js +81 -20
  53. package/dist/esm/breadcrumbs/index.test.js +110 -0
  54. package/dist/esm/breadcrumbs/style.scss +6 -0
  55. package/dist/esm/button/index.js +42 -18
  56. package/dist/esm/button/index.test.js +273 -0
  57. package/dist/esm/card/core-card.js +9 -4
  58. package/dist/esm/card/core-card.test.js +29 -0
  59. package/dist/esm/card/index.js +4 -2
  60. package/dist/esm/card/style-core.scss +59 -0
  61. package/dist/esm/card-feature/index.js +8 -2
  62. package/dist/esm/card-feature/index.test.js +96 -0
  63. package/dist/esm/color-picker/index.js +1 -1
  64. package/dist/esm/confirm-dialog/index.js +26 -9
  65. package/dist/esm/confirm-dialog/index.test.js +176 -0
  66. package/dist/esm/dataviews/index.js +11 -2
  67. package/dist/esm/divider/index.js +14 -0
  68. package/dist/esm/drawer/action.js +68 -0
  69. package/dist/esm/drawer/body.js +70 -0
  70. package/dist/esm/drawer/close-icon.js +37 -0
  71. package/dist/esm/drawer/content.js +63 -0
  72. package/dist/esm/drawer/context.js +17 -0
  73. package/dist/esm/drawer/context.test.js +40 -0
  74. package/dist/esm/drawer/divider.js +16 -0
  75. package/dist/esm/drawer/footer.js +18 -0
  76. package/dist/esm/drawer/header.js +18 -0
  77. package/dist/esm/drawer/index.js +35 -0
  78. package/dist/esm/drawer/index.test.js +1285 -0
  79. package/dist/esm/drawer/root.js +342 -0
  80. package/dist/esm/drawer/slots.test.js +422 -0
  81. package/dist/esm/drawer/style.scss +205 -0
  82. package/dist/esm/drawer/title.js +46 -0
  83. package/dist/esm/drawer/types.js +1 -0
  84. package/dist/esm/drawer/use-exit-animation.js +52 -0
  85. package/dist/esm/hooks/use-confirm-dialog.js +15 -3
  86. package/dist/esm/hooks/use-unsaved-changes-dialog.js +20 -5
  87. package/dist/esm/index.js +4 -0
  88. package/dist/esm/page/index.js +1 -1
  89. package/dist/esm/page-control/index.js +82 -0
  90. package/dist/esm/plugin-settings/SettingsSection.js +8 -2
  91. package/dist/esm/section-header/index.js +5 -2
  92. package/dist/esm/section-header/style.scss +25 -12
  93. package/dist/esm/table-card/index.js +60 -0
  94. package/dist/esm/table-card/index.test.js +150 -0
  95. package/dist/esm/with-wizard-screen/style.scss +8 -0
  96. package/dist/esm/wizard/index.js +104 -38
  97. package/package.json +2 -2
  98. package/src/autocomplete-tokenfield/style.scss +4 -2
  99. package/src/breadcrumbs/format-count.js +27 -0
  100. package/src/breadcrumbs/index.js +68 -8
  101. package/src/breadcrumbs/index.test.js +57 -0
  102. package/src/breadcrumbs/style.scss +6 -0
  103. package/src/button/index.test.js +143 -0
  104. package/src/button/index.tsx +32 -7
  105. package/src/card/core-card.js +15 -2
  106. package/src/card/core-card.test.js +19 -0
  107. package/src/card/index.js +3 -0
  108. package/src/card/style-core.scss +59 -0
  109. package/src/card-feature/README.md +4 -0
  110. package/src/card-feature/index.test.js +65 -0
  111. package/src/card-feature/index.tsx +13 -2
  112. package/src/color-picker/index.js +1 -1
  113. package/src/confirm-dialog/README.md +3 -2
  114. package/src/confirm-dialog/index.test.js +146 -0
  115. package/src/confirm-dialog/index.tsx +23 -9
  116. package/src/dataviews/index.tsx +11 -2
  117. package/src/divider/index.js +13 -0
  118. package/src/drawer/README.md +454 -0
  119. package/src/drawer/action.tsx +67 -0
  120. package/src/drawer/body.tsx +60 -0
  121. package/src/drawer/close-icon.tsx +37 -0
  122. package/src/drawer/content.tsx +54 -0
  123. package/src/drawer/context.test.js +31 -0
  124. package/src/drawer/context.ts +25 -0
  125. package/src/drawer/divider.tsx +13 -0
  126. package/src/drawer/footer.tsx +15 -0
  127. package/src/drawer/header.tsx +15 -0
  128. package/src/drawer/index.test.js +1012 -0
  129. package/src/drawer/index.tsx +33 -0
  130. package/src/drawer/root.tsx +328 -0
  131. package/src/drawer/slots.test.js +314 -0
  132. package/src/drawer/style.scss +205 -0
  133. package/src/drawer/title.tsx +40 -0
  134. package/src/drawer/types.ts +79 -0
  135. package/src/drawer/use-exit-animation.ts +46 -0
  136. package/src/hooks/use-confirm-dialog.tsx +14 -3
  137. package/src/hooks/use-unsaved-changes-dialog.tsx +17 -5
  138. package/src/index.js +4 -0
  139. package/src/page/index.js +1 -1
  140. package/src/page-control/index.js +63 -0
  141. package/src/plugin-settings/SettingsSection.js +8 -2
  142. package/src/section-header/index.js +5 -2
  143. package/src/section-header/style.scss +25 -12
  144. package/src/table-card/README.md +100 -0
  145. package/src/table-card/index.test.js +112 -0
  146. package/src/table-card/index.tsx +56 -0
  147. package/src/with-wizard-screen/style.scss +8 -0
  148. package/src/wizard/index.js +95 -34
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports["default"] = void 0;
8
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
+ var _compose = require("@wordpress/compose");
10
+ var _element = require("@wordpress/element");
11
+ /**
12
+ * WordPress dependencies.
13
+ */
14
+
15
+ // Covers an animationend that never arrives.
16
+ var EXIT_FALLBACK_MS = 400;
17
+ function useExitAnimation(isOpen, node) {
18
+ var _useState = (0, _element.useState)(false),
19
+ _useState2 = (0, _slicedToArray2["default"])(_useState, 2),
20
+ isExiting = _useState2[0],
21
+ setIsExiting = _useState2[1];
22
+ var _useState3 = (0, _element.useState)(isOpen),
23
+ _useState4 = (0, _slicedToArray2["default"])(_useState3, 2),
24
+ wasOpen = _useState4[0],
25
+ setWasOpen = _useState4[1];
26
+
27
+ // The stylesheet zeroes the animation here, so animationend never fires and the
28
+ // fallback would hold a visible panel for 400ms.
29
+ var prefersReducedMotion = (0, _compose.useReducedMotion)();
30
+
31
+ // During render: a commit later the panel would leave the DOM and remount.
32
+ if (wasOpen !== isOpen) {
33
+ setWasOpen(isOpen);
34
+ setIsExiting(!isOpen && !prefersReducedMotion);
35
+ }
36
+ (0, _element.useEffect)(function () {
37
+ if (!isExiting) {
38
+ return;
39
+ }
40
+ // Keyed on the name: animationend bubbles from anything else animating.
41
+ var finish = function finish(event) {
42
+ if (event !== null && event !== void 0 && event.animationName && 'newspack-drawer-slide-out' !== event.animationName) {
43
+ return;
44
+ }
45
+ setIsExiting(false);
46
+ };
47
+ var timer = setTimeout(finish, EXIT_FALLBACK_MS);
48
+ node === null || node === void 0 || node.addEventListener('animationend', finish);
49
+ return function () {
50
+ clearTimeout(timer);
51
+ node === null || node === void 0 || node.removeEventListener('animationend', finish);
52
+ };
53
+ }, [isExiting, node]);
54
+ return {
55
+ isRendered: isOpen || isExiting,
56
+ isExiting: isExiting
57
+ };
58
+ }
59
+ var _default = exports["default"] = useExitAnimation;
@@ -35,6 +35,9 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
35
35
  *
36
36
  * The `confirmDialog` element must be rendered somewhere in the component's
37
37
  * JSX for the dialog to appear.
38
+ *
39
+ * `cancelConfirm()` withdraws a pending request without invoking its callback,
40
+ * for when whatever raised the prompt goes away on its own.
38
41
  */
39
42
  function useConfirmDialog(options) {
40
43
  var message = options.message,
@@ -59,12 +62,20 @@ function useConfirmDialog(options) {
59
62
  callback();
60
63
  }
61
64
  }, []);
65
+ var cancelConfirm = (0, _element.useCallback)(function () {
66
+ return setPendingAction(null);
67
+ }, []);
62
68
  var confirmDialog = /*#__PURE__*/(0, _jsxRuntime.jsx)(_confirmDialog["default"], _objectSpread(_objectSpread({}, dialogProps), {}, {
63
69
  when: when,
64
70
  isOpen: !!pendingAction,
65
71
  onConfirm: function onConfirm() {
66
- pendingAction === null || pendingAction === void 0 || pendingAction();
67
- setPendingAction(null);
72
+ // Cleared either way: a throwing callback would otherwise leave the
73
+ // dialog permanently unraisable for this instance.
74
+ try {
75
+ pendingAction === null || pendingAction === void 0 || pendingAction();
76
+ } finally {
77
+ setPendingAction(null);
78
+ }
68
79
  },
69
80
  onCancel: function onCancel() {
70
81
  return setPendingAction(null);
@@ -73,7 +84,8 @@ function useConfirmDialog(options) {
73
84
  }));
74
85
  return {
75
86
  confirmDialog: confirmDialog,
76
- requestConfirm: requestConfirm
87
+ requestConfirm: requestConfirm,
88
+ cancelConfirm: cancelConfirm
77
89
  };
78
90
  }
79
91
  var _default = exports["default"] = useConfirmDialog;
@@ -42,7 +42,8 @@ function isSameOriginNavigation(link) {
42
42
  * messaging, intercepts same-origin link clicks so the dialog fires instead
43
43
  * of a silent navigation, and adds a `beforeunload` listener as the last-resort
44
44
  * guard for refresh / tab-close (browser-native, cannot be styled). The
45
- * returned `confirmDialog` element must be rendered in JSX.
45
+ * returned `confirmDialog` element must be rendered in JSX, and `requestConfirm`
46
+ * and `cancelConfirm` behave as `useConfirmDialog` documents them.
46
47
  *
47
48
  * Single-consumer constraint: the click handler is attached at the document
48
49
  * level in capture phase. Two simultaneously-active instances will both fire
@@ -53,11 +54,14 @@ function useUnsavedChangesDialog(_ref) {
53
54
  var _useConfirmDialog = (0, _useConfirmDialog2["default"])({
54
55
  when: when,
55
56
  message: (0, _i18n.__)('You have unsaved changes that will be lost. Discard changes?', 'newspack-plugin'),
56
- confirmButtonText: (0, _i18n.__)('Discard changes', 'newspack-plugin'),
57
+ confirmButtonText: (0, _i18n.__)('Discard Changes', 'newspack-plugin'),
58
+ // Hidden, but still the dialog's accessible name.
59
+ title: (0, _i18n.__)('Unsaved changes', 'newspack-plugin'),
57
60
  hideTitle: true
58
61
  }),
59
62
  confirmDialog = _useConfirmDialog.confirmDialog,
60
- requestConfirm = _useConfirmDialog.requestConfirm;
63
+ requestConfirm = _useConfirmDialog.requestConfirm,
64
+ cancelConfirm = _useConfirmDialog.cancelConfirm;
61
65
 
62
66
  // Tracks navigation the user has already approved via our custom dialog so
63
67
  // the beforeunload guard doesn't fire a second native prompt on top of it.
@@ -136,9 +140,20 @@ function useUnsavedChangesDialog(_ref) {
136
140
  return window.removeEventListener('beforeunload', handler);
137
141
  };
138
142
  }, [when]);
143
+
144
+ // Escape hatch for an action that navigates on its own — a handoff that
145
+ // POSTs and then assigns `window.location` — once the dialog has already
146
+ // been confirmed. Left armed, the beforeunload guard would draw a second,
147
+ // native prompt on top of it. Call it immediately before navigating, so a
148
+ // failed action leaves the guard in place.
149
+ var allowNextUnload = (0, _element.useCallback)(function () {
150
+ isNavigatingRef.current = true;
151
+ }, []);
139
152
  return {
140
153
  confirmDialog: confirmDialog,
141
- requestConfirm: requestConfirm
154
+ requestConfirm: requestConfirm,
155
+ cancelConfirm: cancelConfirm,
156
+ allowNextUnload: allowNextUnload
142
157
  };
143
158
  }
144
159
  var _default = exports["default"] = useUnsavedChangesDialog;
package/dist/cjs/index.js CHANGED
@@ -130,6 +130,12 @@ Object.defineProperty(exports, "Divider", {
130
130
  return _divider["default"];
131
131
  }
132
132
  });
133
+ Object.defineProperty(exports, "Drawer", {
134
+ enumerable: true,
135
+ get: function get() {
136
+ return _drawer["default"];
137
+ }
138
+ });
133
139
  Object.defineProperty(exports, "Footer", {
134
140
  enumerable: true,
135
141
  get: function get() {
@@ -208,6 +214,12 @@ Object.defineProperty(exports, "Page", {
208
214
  return _page["default"];
209
215
  }
210
216
  });
217
+ Object.defineProperty(exports, "PageControl", {
218
+ enumerable: true,
219
+ get: function get() {
220
+ return _pageControl["default"];
221
+ }
222
+ });
211
223
  Object.defineProperty(exports, "PluginInstaller", {
212
224
  enumerable: true,
213
225
  get: function get() {
@@ -292,6 +304,12 @@ Object.defineProperty(exports, "TabbedNavigation", {
292
304
  return _tabbedNavigation["default"];
293
305
  }
294
306
  });
307
+ Object.defineProperty(exports, "TableCard", {
308
+ enumerable: true,
309
+ get: function get() {
310
+ return _tableCard["default"];
311
+ }
312
+ });
295
313
  Object.defineProperty(exports, "TextControl", {
296
314
  enumerable: true,
297
315
  get: function get() {
@@ -316,6 +334,12 @@ Object.defineProperty(exports, "Wizard", {
316
334
  return _wizard["default"];
317
335
  }
318
336
  });
337
+ Object.defineProperty(exports, "WizardBanner", {
338
+ enumerable: true,
339
+ get: function get() {
340
+ return _wizard.WizardBanner;
341
+ }
342
+ });
319
343
  Object.defineProperty(exports, "espProviderOrder", {
320
344
  enumerable: true,
321
345
  get: function get() {
@@ -380,6 +404,7 @@ var _confirmDialog = _interopRequireDefault(require("./confirm-dialog"));
380
404
  var _customSelectControl = _interopRequireDefault(require("./custom-select-control"));
381
405
  var _dataviews = _interopRequireDefault(require("./dataviews"));
382
406
  var _divider = _interopRequireDefault(require("./divider"));
407
+ var _drawer = _interopRequireDefault(require("./drawer"));
383
408
  var _formTokenField = _interopRequireDefault(require("./form-token-field"));
384
409
  var _footer = _interopRequireDefault(require("./footer"));
385
410
  var _handoff = _interopRequireDefault(require("./handoff"));
@@ -392,6 +417,7 @@ var _modal = _interopRequireDefault(require("./modal"));
392
417
  var _newspackIcon = _interopRequireDefault(require("./newspack-icon"));
393
418
  var _notice = _interopRequireDefault(require("./notice"));
394
419
  var _page = _interopRequireDefault(require("./page"));
420
+ var _pageControl = _interopRequireDefault(require("./page-control"));
395
421
  var _pluginInstaller = _interopRequireDefault(require("./plugin-installer"));
396
422
  var _pluginSettings = _interopRequireDefault(require("./plugin-settings"));
397
423
  var _pluginToggle = _interopRequireDefault(require("./plugin-toggle"));
@@ -405,10 +431,11 @@ var _stepsList = _interopRequireDefault(require("./steps-list"));
405
431
  var _stepsListItem = _interopRequireDefault(require("./steps-list-item"));
406
432
  var _styleCard = _interopRequireDefault(require("./style-card"));
407
433
  var _tabbedNavigation = _interopRequireDefault(require("./tabbed-navigation"));
434
+ var _tableCard = _interopRequireDefault(require("./table-card"));
408
435
  var _textControl = _interopRequireDefault(require("./text-control"));
409
436
  var _waiting = _interopRequireDefault(require("./waiting"));
410
437
  var _webPreview = _interopRequireDefault(require("./web-preview"));
411
- var _wizard = _interopRequireDefault(require("./wizard"));
438
+ var _wizard = _interopRequireWildcard(require("./wizard"));
412
439
  var _withWizard = _interopRequireDefault(require("./with-wizard"));
413
440
  var _withWizardScreen = _interopRequireDefault(require("./with-wizard-screen"));
414
441
  var _router = _interopRequireDefault(require("./proxied-imports/router"));
@@ -49,7 +49,7 @@ var _jsxRuntime = require("react/jsx-runtime");
49
49
  * inside its active tab panel — or, when no visible tab
50
50
  * owns the route, as a sibling of the panels.
51
51
  * @param {string} [props.className]
52
- * @param {*} props.children
52
+ * @param {*} [props.children]
53
53
  * @return {JSX.Element} Page component.
54
54
  */var Page = function Page(_ref) {
55
55
  var _breadcrumbItems;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports["default"] = void 0;
8
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
+ var _element = require("@wordpress/element");
11
+ var _htmlEntities = require("@wordpress/html-entities");
12
+ var _i18n = require("@wordpress/i18n");
13
+ var _ = require("../");
14
+ var _jsxRuntime = require("react/jsx-runtime");
15
+ 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; }
16
+ 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) { (0, _defineProperty2["default"])(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; } /**
17
+ * Page Control.
18
+ *
19
+ * A single-page selector backed by an autocomplete search, for settings that point at a
20
+ * page without enumerating every page the site has.
21
+ */ /**
22
+ * WordPress dependencies
23
+ */ /**
24
+ * Internal dependencies
25
+ */ /**
26
+ * Render a single-page selector.
27
+ *
28
+ * @param {Object} props Component props.
29
+ * @param {string} props.label Field label.
30
+ * @param {string} props.help Help text.
31
+ * @param {boolean} props.disabled Whether the field is disabled (e.g. while saving).
32
+ * @param {Object} props.selected The saved page as `{ label, value }`, if any.
33
+ * @param {Function} props.onChange Called with the selected page ID, or '' when cleared.
34
+ */
35
+ var PageControl = function PageControl(_ref) {
36
+ var label = _ref.label,
37
+ help = _ref.help,
38
+ disabled = _ref.disabled,
39
+ selected = _ref.selected,
40
+ _onChange = _ref.onChange;
41
+ // Titles arrive HTML-encoded from the server, while the autocomplete decodes the
42
+ // suggestions it fetches. Decode here so the saved page and the search results below
43
+ // it render the same way.
44
+ var normalize = function normalize(item) {
45
+ return item ? _objectSpread(_objectSpread({}, item), {}, {
46
+ label: (0, _htmlEntities.decodeEntities)(item.label)
47
+ }) : null;
48
+ };
49
+ var _useState = (0, _element.useState)(normalize(selected)),
50
+ _useState2 = (0, _slicedToArray2["default"])(_useState, 2),
51
+ selectedItem = _useState2[0],
52
+ setSelectedItem = _useState2[1];
53
+ // Settings are fetched after this control first renders, so adopt the saved page
54
+ // whenever it arrives or changes rather than only on mount.
55
+ (0, _element.useEffect)(function () {
56
+ setSelectedItem(normalize(selected));
57
+ }, [selected === null || selected === void 0 ? void 0 : selected.value, selected === null || selected === void 0 ? void 0 : selected.label]);
58
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)("fieldset", {
59
+ disabled: disabled,
60
+ className: "newspack-page-control",
61
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_.AutocompleteWithSuggestions, {
62
+ label: label,
63
+ help: help,
64
+ postTypes: [{
65
+ slug: 'page',
66
+ label: (0, _i18n.__)('Page', 'newspack-plugin')
67
+ }],
68
+ postTypeLabel: (0, _i18n.__)('page', 'newspack-plugin'),
69
+ postTypeLabelPlural: (0, _i18n.__)('pages', 'newspack-plugin'),
70
+ selectedItems: selectedItem ? [selectedItem] : [],
71
+ onChange: function onChange(items) {
72
+ if (disabled) {
73
+ return;
74
+ }
75
+ var item = items && items.length ? items[items.length - 1] : null;
76
+ setSelectedItem(normalize(item));
77
+ _onChange(item ? String(item.value) : '');
78
+ }
79
+ })
80
+ });
81
+ };
82
+ var _default = exports["default"] = PageControl;
@@ -26,6 +26,10 @@ var isSelectControl = function isSelectControl(setting) {
26
26
  return Array.isArray(setting.options) && setting.options.length;
27
27
  };
28
28
  var getControlComponent = function getControlComponent(setting) {
29
+ // An explicit `control` overrides the control implied by the setting's data type.
30
+ if ('page' === setting.control) {
31
+ return _.PageControl;
32
+ }
29
33
  if (isSelectControl(setting)) {
30
34
  return _.SelectControl;
31
35
  }
@@ -86,6 +90,8 @@ var SettingsSection = function SettingsSection(props) {
86
90
  };
87
91
  })) || null,
88
92
  value: setting.value,
93
+ // Only page controls consume `selected`; keep it off every other control's props.
94
+ selected: 'page' === setting.control ? setting.selected || null : null,
89
95
  multiple: isSelectControl(setting) && setting.multiple ? true : null,
90
96
  checked: setting.type === 'boolean' ? !!setting.value : null,
91
97
  onChange: function onChange(value) {
@@ -124,7 +130,7 @@ var SettingsSection = function SettingsSection(props) {
124
130
  onUpdate();
125
131
  setSaveDisabled(true);
126
132
  },
127
- children: (0, _i18n.__)('Save Settings', 'newspack')
133
+ children: (0, _i18n.__)('Save Settings', 'newspack-plugin')
128
134
  })),
129
135
  children: (active || active === null) && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_element.Fragment, {
130
136
  children: [(error === null || error === void 0 ? void 0 : error.message) && /*#__PURE__*/(0, _jsxRuntime.jsx)(_.Notice, {
@@ -46,6 +46,7 @@ var _jsxRuntime = require("react/jsx-runtime");
46
46
  * @property {boolean} [isWhite=false] - Indicates if the header should use a white theme.
47
47
  * @property {boolean} [noMargin=false] - Indicates if the header should have no margin.
48
48
  * @property {boolean} [pageHeader=false] - Indicates if the header is used as a page header.
49
+ * @property {string} [size='default'] - Size variant, either 'default' or 'small'. Scales the title, and the icon with it, independently of `pageHeader`.
49
50
  * @property {string} title - The title of the section.
50
51
  * @property {?string} [id=null] - Optional ID for the header element.
51
52
  * @property {?string|Function|*} [children=null] - Optional children to display in the header.
@@ -75,6 +76,8 @@ var _jsxRuntime = require("react/jsx-runtime");
75
76
  noMargin = _ref$noMargin === void 0 ? false : _ref$noMargin,
76
77
  _ref$pageHeader = _ref.pageHeader,
77
78
  pageHeader = _ref$pageHeader === void 0 ? false : _ref$pageHeader,
79
+ _ref$size = _ref.size,
80
+ size = _ref$size === void 0 ? 'default' : _ref$size,
78
81
  title = _ref.title,
79
82
  _ref$id = _ref.id,
80
83
  id = _ref$id === void 0 ? null : _ref$id,
@@ -101,7 +104,7 @@ var _jsxRuntime = require("react/jsx-runtime");
101
104
  }, 250);
102
105
  }
103
106
  }, []);
104
- var classes = (0, _classnames["default"])('newspack-section-header', centered && 'newspack-section-header--is-centered', isWhite && 'newspack-section-header--is-white', noMargin && 'newspack-section-header--no-margin', pageHeader && 'newspack-section-header--page-header');
107
+ var classes = (0, _classnames["default"])('newspack-section-header', centered && 'newspack-section-header--is-centered', isWhite && 'newspack-section-header--is-white', noMargin && 'newspack-section-header--no-margin', pageHeader && 'newspack-section-header--page-header', size === 'small' && 'newspack-section-header--small');
105
108
 
106
109
  // The breadcrumb `Page` owns the single page `<h1>`, so a `pageHeader` section
107
110
  // is a secondary heading: its level follows `heading` (default 2). `pageHeader`
@@ -164,7 +167,7 @@ var _jsxRuntime = require("react/jsx-runtime");
164
167
  className: "newspack-section-header__icon",
165
168
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.Icon, {
166
169
  icon: icon,
167
- size: 48
170
+ size: size === 'small' ? 24 : 48
168
171
  })
169
172
  }), backNav ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.__experimentalHStack, {
170
173
  alignment: "left",
@@ -95,6 +95,31 @@
95
95
  @include wp-mixins.heading-x-large();
96
96
  }
97
97
 
98
+ &#{&}--small {
99
+ .newspack-section-header__title {
100
+ @include wp-mixins.heading-large();
101
+ }
102
+ }
103
+
104
+ &__icon {
105
+ background-color: var(--wp-admin-theme-color-lighter-10);
106
+ border-radius: 100%;
107
+ display: grid;
108
+ height: 80px;
109
+ margin-bottom: 8px !important;
110
+ width: 80px;
111
+ place-items: center;
112
+ svg {
113
+ fill: var(--wp-admin-theme-color);
114
+ }
115
+ }
116
+
117
+ &#{&}--small .newspack-section-header__icon {
118
+ height: wp-vars.$grid-unit-50;
119
+ margin-bottom: wp-vars.$grid-unit-05 !important;
120
+ width: wp-vars.$grid-unit-50;
121
+ }
122
+
98
123
  &__title-container {
99
124
  align-items: center;
100
125
  display: flex;
@@ -154,17 +179,5 @@
154
179
  > p {
155
180
  text-align: center;
156
181
  }
157
- .newspack-section-header__icon {
158
- background-color: var(--wp-admin-theme-color-lighter-10);
159
- border-radius: 100%;
160
- display: grid;
161
- height: 80px;
162
- margin-bottom: 8px !important;
163
- width: 80px;
164
- place-items: center;
165
- svg {
166
- fill: var(--wp-admin-theme-color);
167
- }
168
- }
169
182
  }
170
183
  }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports["default"] = TableCard;
7
+ var _ui = require("@wordpress/ui");
8
+ var _jsxRuntime = require("react/jsx-runtime");
9
+ /**
10
+ * TableCard
11
+ *
12
+ * The DataViews-in-a-card composition from @wordpress/ui. Which card edges the
13
+ * table touches follows upstream first/last-child rules, so it changes with the
14
+ * slots present — the README spells the combinations out.
15
+ */
16
+
17
+ /**
18
+ * WordPress dependencies
19
+ */
20
+
21
+ /** `0` is a renderable slot value; the falsy leftovers of `cond && <X />` are not. */
22
+ var isRenderable = function isRenderable(node) {
23
+ return node !== undefined && node !== null && node !== '' && typeof node !== 'boolean';
24
+ };
25
+ function TableCard(_ref) {
26
+ var title = _ref.title,
27
+ titleId = _ref.titleId,
28
+ _ref$heading = _ref.heading,
29
+ heading = _ref$heading === void 0 ? 3 : _ref$heading,
30
+ actions = _ref.actions,
31
+ before = _ref.before,
32
+ after = _ref.after,
33
+ className = _ref.className,
34
+ children = _ref.children;
35
+ var TitleTag = "h".concat(heading);
36
+ var hasTitle = isRenderable(title);
37
+ var hasActions = isRenderable(actions);
38
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_ui.Card.Root, {
39
+ className: className,
40
+ children: [(hasTitle || hasActions) && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_ui.Card.Header, {
41
+ render: hasActions ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_ui.Stack, {
42
+ direction: "row",
43
+ justify: hasTitle ? 'space-between' : 'end',
44
+ align: "center"
45
+ }) : undefined,
46
+ children: [hasTitle && /*#__PURE__*/(0, _jsxRuntime.jsx)(_ui.Card.Title, {
47
+ render: /*#__PURE__*/(0, _jsxRuntime.jsx)(TitleTag, {
48
+ id: titleId
49
+ }),
50
+ children: title
51
+ }), hasActions && /*#__PURE__*/(0, _jsxRuntime.jsx)(_ui.Stack, {
52
+ direction: "row",
53
+ gap: "sm",
54
+ align: "center",
55
+ children: actions
56
+ })]
57
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_ui.Card.Content, {
58
+ render: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ui.Stack, {
59
+ direction: "column",
60
+ gap: "lg"
61
+ }),
62
+ children: [before, /*#__PURE__*/(0, _jsxRuntime.jsx)(_ui.Card.FullBleed, {
63
+ children: children
64
+ }), after]
65
+ })]
66
+ });
67
+ }
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _react = require("@testing-library/react");
5
+ var _ = _interopRequireDefault(require("."));
6
+ var _jsxRuntime = require("react/jsx-runtime");
7
+ /**
8
+ * External dependencies
9
+ */
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+
15
+ // @wordpress/ui injects no CSS under Jest, so the layout this component exists
16
+ // for (full bleed, edge bumps, the content gap) is invisible here; slot order
17
+ // and heading semantics are the assertable contract.
18
+ describe('TableCard', function () {
19
+ it('renders the table children inside the card', function () {
20
+ (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
21
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("table", {
22
+ "aria-label": "Prices"
23
+ })
24
+ }));
25
+ expect(_react.screen.getByRole('table', {
26
+ name: 'Prices'
27
+ })).toBeInTheDocument();
28
+ });
29
+ it('renders the title as a real heading carrying the given id', function () {
30
+ var _render = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
31
+ title: "Price schedule",
32
+ titleId: "tc-title",
33
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("table", {
34
+ "aria-label": "Prices"
35
+ })
36
+ })),
37
+ rerender = _render.rerender;
38
+ expect(_react.screen.getByRole('heading', {
39
+ level: 3,
40
+ name: 'Price schedule'
41
+ })).toHaveAttribute('id', 'tc-title');
42
+ rerender(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
43
+ title: "Price schedule",
44
+ heading: 2,
45
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("table", {
46
+ "aria-label": "Prices"
47
+ })
48
+ }));
49
+ expect(_react.screen.getByRole('heading', {
50
+ level: 2,
51
+ name: 'Price schedule'
52
+ })).toBeInTheDocument();
53
+ rerender(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
54
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("table", {
55
+ "aria-label": "Prices"
56
+ })
57
+ }));
58
+ expect(_react.screen.queryByRole('heading')).not.toBeInTheDocument();
59
+ });
60
+ it('treats 0 as a renderable title and the empty string as none', function () {
61
+ var _render2 = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
62
+ title: 0,
63
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("table", {
64
+ "aria-label": "Prices"
65
+ })
66
+ })),
67
+ rerender = _render2.rerender;
68
+ expect(_react.screen.getByRole('heading', {
69
+ level: 3,
70
+ name: '0'
71
+ })).toBeInTheDocument();
72
+ rerender(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
73
+ title: "",
74
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("table", {
75
+ "aria-label": "Prices"
76
+ })
77
+ }));
78
+ expect(_react.screen.queryByRole('heading')).not.toBeInTheDocument();
79
+ });
80
+ it('renders header actions alongside the title', function () {
81
+ (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
82
+ title: "Price Schedule",
83
+ actions: /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
84
+ children: "Add Price"
85
+ }),
86
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("table", {
87
+ "aria-label": "Prices"
88
+ })
89
+ }));
90
+ var heading = _react.screen.getByRole('heading', {
91
+ level: 3,
92
+ name: 'Price Schedule'
93
+ });
94
+ var action = _react.screen.getByRole('button', {
95
+ name: 'Add Price'
96
+ });
97
+ expect(heading.parentElement).toContainElement(action);
98
+ });
99
+ it('renders a header for actions alone, and none for boolean slot leftovers', function () {
100
+ var _render3 = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
101
+ actions: /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
102
+ children: "Add Price"
103
+ }),
104
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("table", {
105
+ "aria-label": "Prices"
106
+ })
107
+ })),
108
+ rerender = _render3.rerender;
109
+ expect(_react.screen.getByRole('button', {
110
+ name: 'Add Price'
111
+ })).toBeInTheDocument();
112
+ expect(_react.screen.queryByRole('heading')).not.toBeInTheDocument();
113
+ rerender(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
114
+ title: false,
115
+ actions: false,
116
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("table", {
117
+ "aria-label": "Prices"
118
+ })
119
+ }));
120
+ expect(_react.screen.queryByRole('heading')).not.toBeInTheDocument();
121
+ expect(_react.screen.queryByRole('button')).not.toBeInTheDocument();
122
+ });
123
+ it('orders before, table, after within the card body', function () {
124
+ (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
125
+ before: /*#__PURE__*/(0, _jsxRuntime.jsx)("p", {
126
+ children: "Stats row"
127
+ }),
128
+ after: /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
129
+ children: "See More"
130
+ }),
131
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("table", {
132
+ "aria-label": "Prices"
133
+ })
134
+ }));
135
+ var before = _react.screen.getByText('Stats row');
136
+ var after = _react.screen.getByRole('button', {
137
+ name: 'See More'
138
+ });
139
+ var table = _react.screen.getByRole('table', {
140
+ name: 'Prices'
141
+ });
142
+ // Walk the table up to the card body's direct child, so extra upstream
143
+ // wrappers cannot silently repoint the comparison.
144
+ var body = before.parentElement;
145
+ var tableWrap = table;
146
+ while (tableWrap.parentElement !== body) {
147
+ tableWrap = tableWrap.parentElement;
148
+ }
149
+ var order = Array.from(body.children);
150
+ expect(order.indexOf(before)).toBeLessThan(order.indexOf(tableWrap));
151
+ expect(order.indexOf(tableWrap)).toBeLessThan(order.indexOf(after));
152
+ });
153
+ });
@@ -18,6 +18,14 @@
18
18
 
19
19
  color: wp-colors.$gray-900;
20
20
 
21
+ // Full-width dividers span 100vw, which overspills the admin content area
22
+ // (admin menu offset + scrollbar) and triggers horizontal scrolling; clip
23
+ // (not hidden) so no scroll container is created. Scoped off __content,
24
+ // which can be a constrained centered column.
25
+ &:not(.newspack-wizard__content) {
26
+ overflow-x: clip;
27
+ }
28
+
21
29
  // Headings
22
30
  h1,
23
31
  h2,