newspack-components 4.5.0 → 4.6.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 (138) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/DEVELOPMENT.md +1 -1
  3. package/dist/cjs/accordion/index.js +43 -26
  4. package/dist/cjs/accordion/index.test.js +80 -0
  5. package/dist/cjs/accordion/style.scss +25 -21
  6. package/dist/cjs/action-card/style.scss +0 -22
  7. package/dist/cjs/breadcrumbs/index.js +80 -0
  8. package/dist/cjs/breadcrumbs/index.test.js +109 -0
  9. package/dist/cjs/breadcrumbs/style.scss +41 -0
  10. package/dist/cjs/card/core-card.js +8 -4
  11. package/dist/cjs/card/index.js +1 -1
  12. package/dist/cjs/card/style-core.scss +14 -0
  13. package/dist/cjs/card/style.scss +0 -9
  14. package/dist/cjs/card-feature/index.js +25 -13
  15. package/dist/cjs/card-settings-group/index.js +3 -0
  16. package/dist/cjs/confirm-dialog/index.js +34 -8
  17. package/dist/cjs/confirm-dialog/index.test.js +173 -0
  18. package/dist/cjs/divider/style.scss +4 -2
  19. package/dist/cjs/grid/style.scss +5 -0
  20. package/dist/cjs/handoff/index.js +8 -5
  21. package/dist/cjs/index.js +34 -1
  22. package/dist/cjs/integration-icon/index.js +40 -0
  23. package/dist/cjs/integration-icon/style.scss +42 -0
  24. package/dist/cjs/integration-icons/index.js +21 -2
  25. package/dist/cjs/modal/style.scss +4 -0
  26. package/dist/cjs/page/index.js +121 -0
  27. package/dist/cjs/page/index.test.js +69 -0
  28. package/dist/cjs/page/style.scss +86 -0
  29. package/dist/cjs/route-match.js +59 -0
  30. package/dist/cjs/route-match.test.js +77 -0
  31. package/dist/cjs/section-header/index.js +8 -1
  32. package/dist/cjs/section-header/style.scss +11 -11
  33. package/dist/cjs/select-control/GroupedSelectControl.js +1 -1
  34. package/dist/cjs/select-control/style.scss +4 -0
  35. package/dist/cjs/style.scss +1 -4
  36. package/dist/cjs/tabbed-navigation/index.js +188 -40
  37. package/dist/cjs/tabbed-navigation/index.test.js +299 -0
  38. package/dist/cjs/tabbed-navigation/style.scss +77 -57
  39. package/dist/cjs/with-wizard/index.js +34 -45
  40. package/dist/cjs/with-wizard-screen/index.js +82 -41
  41. package/dist/cjs/with-wizard-screen/index.test.js +50 -0
  42. package/dist/cjs/with-wizard-screen/style.scss +15 -118
  43. package/dist/cjs/wizard/breadcrumbs-select.js +70 -0
  44. package/dist/cjs/wizard/breadcrumbs-select.test.js +200 -0
  45. package/dist/cjs/wizard/components/WizardError.js +8 -4
  46. package/dist/cjs/wizard/index.js +162 -141
  47. package/dist/esm/accordion/index.js +42 -26
  48. package/dist/esm/accordion/index.test.js +79 -0
  49. package/dist/esm/accordion/style.scss +25 -21
  50. package/dist/esm/action-card/style.scss +0 -22
  51. package/dist/esm/breadcrumbs/index.js +75 -0
  52. package/dist/esm/breadcrumbs/index.test.js +105 -0
  53. package/dist/esm/breadcrumbs/style.scss +41 -0
  54. package/dist/esm/card/core-card.js +8 -4
  55. package/dist/esm/card/index.js +1 -1
  56. package/dist/esm/card/style-core.scss +14 -0
  57. package/dist/esm/card/style.scss +0 -9
  58. package/dist/esm/card-feature/index.js +25 -13
  59. package/dist/esm/card-settings-group/index.js +3 -0
  60. package/dist/esm/confirm-dialog/index.js +34 -8
  61. package/dist/esm/confirm-dialog/index.test.js +173 -0
  62. package/dist/esm/divider/style.scss +4 -2
  63. package/dist/esm/grid/style.scss +5 -0
  64. package/dist/esm/handoff/index.js +7 -5
  65. package/dist/esm/index.js +5 -1
  66. package/dist/esm/integration-icon/index.js +34 -0
  67. package/dist/esm/integration-icon/style.scss +42 -0
  68. package/dist/esm/integration-icons/index.js +22 -1
  69. package/dist/esm/modal/style.scss +4 -0
  70. package/dist/esm/page/index.js +113 -0
  71. package/dist/esm/page/index.test.js +65 -0
  72. package/dist/esm/page/style.scss +86 -0
  73. package/dist/esm/route-match.js +51 -0
  74. package/dist/esm/route-match.test.js +74 -0
  75. package/dist/esm/section-header/index.js +8 -1
  76. package/dist/esm/section-header/style.scss +11 -11
  77. package/dist/esm/select-control/GroupedSelectControl.js +1 -1
  78. package/dist/esm/select-control/style.scss +4 -0
  79. package/dist/esm/style.scss +1 -4
  80. package/dist/esm/tabbed-navigation/index.js +192 -38
  81. package/dist/esm/tabbed-navigation/index.test.js +296 -0
  82. package/dist/esm/tabbed-navigation/style.scss +77 -57
  83. package/dist/esm/with-wizard/index.js +33 -45
  84. package/dist/esm/with-wizard-screen/index.js +78 -41
  85. package/dist/esm/with-wizard-screen/index.test.js +46 -0
  86. package/dist/esm/with-wizard-screen/style.scss +15 -118
  87. package/dist/esm/wizard/breadcrumbs-select.js +63 -0
  88. package/dist/esm/wizard/breadcrumbs-select.test.js +197 -0
  89. package/dist/esm/wizard/components/WizardError.js +7 -5
  90. package/dist/esm/wizard/index.js +165 -144
  91. package/package.json +10 -2
  92. package/src/accordion/index.js +27 -15
  93. package/src/accordion/index.test.js +62 -0
  94. package/src/accordion/style.scss +25 -21
  95. package/src/action-card/style.scss +0 -22
  96. package/src/breadcrumbs/index.js +63 -0
  97. package/src/breadcrumbs/index.test.js +59 -0
  98. package/src/breadcrumbs/style.scss +41 -0
  99. package/src/card/core-card.js +11 -6
  100. package/src/card/index.js +0 -1
  101. package/src/card/style-core.scss +14 -0
  102. package/src/card/style.scss +0 -9
  103. package/src/card-feature/README.md +3 -3
  104. package/src/card-feature/index.tsx +37 -21
  105. package/src/card-settings-group/index.tsx +4 -0
  106. package/src/card-sortable-list/index.tsx +1 -1
  107. package/src/confirm-dialog/index.test.js +142 -0
  108. package/src/confirm-dialog/index.tsx +34 -7
  109. package/src/divider/style.scss +4 -2
  110. package/src/grid/style.scss +5 -0
  111. package/src/handoff/index.js +4 -3
  112. package/src/index.js +5 -1
  113. package/src/integration-icon/index.tsx +35 -0
  114. package/src/integration-icon/style.scss +42 -0
  115. package/src/integration-icons/index.js +23 -0
  116. package/src/modal/style.scss +4 -0
  117. package/src/page/index.js +89 -0
  118. package/src/page/index.test.js +43 -0
  119. package/src/page/style.scss +86 -0
  120. package/src/route-match.js +47 -0
  121. package/src/route-match.test.js +50 -0
  122. package/src/section-header/index.js +7 -3
  123. package/src/section-header/style.scss +11 -11
  124. package/src/select-control/GroupedSelectControl.js +1 -1
  125. package/src/select-control/style.scss +4 -0
  126. package/src/style.scss +1 -4
  127. package/src/tabbed-navigation/index.js +181 -34
  128. package/src/tabbed-navigation/index.test.js +228 -0
  129. package/src/tabbed-navigation/style.scss +77 -57
  130. package/src/with-wizard/index.js +18 -39
  131. package/src/with-wizard-screen/index.js +70 -35
  132. package/src/with-wizard-screen/index.test.js +36 -0
  133. package/src/with-wizard-screen/style.scss +15 -118
  134. package/src/wizard/breadcrumbs-select.js +51 -0
  135. package/src/wizard/breadcrumbs-select.test.js +124 -0
  136. package/src/wizard/components/WizardError.js +4 -3
  137. package/src/wizard/index.js +160 -146
  138. package/tsconfig.json +7 -0
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports["default"] = void 0;
8
8
  var _classnames = _interopRequireDefault(require("classnames"));
9
9
  var _i18n = require("@wordpress/i18n");
10
+ var _element = require("@wordpress/element");
10
11
  var _components = require("@wordpress/components");
11
12
  var _icons = require("@wordpress/icons");
12
13
  var _badge = _interopRequireDefault(require("../badge"));
@@ -45,6 +46,8 @@ var CardFeature = function CardFeature(_ref) {
45
46
  _ref$requirementsActi = _ref.requirementsActionable,
46
47
  requirementsActionable = _ref$requirementsActi === void 0 ? false : _ref$requirementsActi,
47
48
  enableLabel = _ref.enableLabel,
49
+ _ref$busy = _ref.busy,
50
+ busy = _ref$busy === void 0 ? false : _ref$busy,
48
51
  configureLabel = _ref.configureLabel,
49
52
  onEnable = _ref.onEnable,
50
53
  onConfigure = _ref.onConfigure,
@@ -71,6 +74,7 @@ var CardFeature = function CardFeature(_ref) {
71
74
  }
72
75
  var isConfigureState = enabled && !requirements;
73
76
  var buttonLabel = isConfigureState ? configureLabel !== null && configureLabel !== void 0 ? configureLabel : (0, _i18n.__)('Configure', 'newspack-plugin') : enableLabel !== null && enableLabel !== void 0 ? enableLabel : (0, _i18n.__)('Enable', 'newspack-plugin');
77
+ var showMoreControls = enabled && !!(moreControls !== null && moreControls !== void 0 && moreControls.length) && (!requirements || requirementsActionable);
74
78
  var handleButtonClick = function handleButtonClick() {
75
79
  if (isConfigureState) {
76
80
  onConfigure === null || onConfigure === void 0 || onConfigure();
@@ -78,10 +82,24 @@ var CardFeature = function CardFeature(_ref) {
78
82
  onEnable === null || onEnable === void 0 || onEnable();
79
83
  }
80
84
  };
81
- var iconClasses = icon ? (0, _classnames["default"])('newspack-card-feature__icon', {
82
- 'newspack-card-feature__icon--radius-small': !!icon.backgroundColor && icon.radius !== 'full',
83
- 'newspack-card-feature__icon--radius-full': icon.radius === 'full'
85
+ var iconDescriptor = icon && !(0, _element.isValidElement)(icon) ? icon : null;
86
+ var iconClasses = iconDescriptor ? (0, _classnames["default"])('newspack-card-feature__icon', {
87
+ 'newspack-card-feature__icon--radius-small': !!iconDescriptor.backgroundColor && iconDescriptor.radius !== 'full',
88
+ 'newspack-card-feature__icon--radius-full': iconDescriptor.radius === 'full'
84
89
  }) : undefined;
90
+ var renderedIcon = null;
91
+ if ((0, _element.isValidElement)(icon)) {
92
+ renderedIcon = icon;
93
+ } else if (iconDescriptor) {
94
+ renderedIcon = /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
95
+ className: iconClasses,
96
+ style: {
97
+ backgroundColor: iconDescriptor.backgroundColor,
98
+ color: iconDescriptor.fill
99
+ },
100
+ children: iconDescriptor.node
101
+ });
102
+ }
85
103
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_card["default"], {
86
104
  className: classes,
87
105
  __experimentalCoreCard: true,
@@ -102,14 +120,7 @@ var CardFeature = function CardFeature(_ref) {
102
120
  className: "newspack-card-feature__description",
103
121
  children: description
104
122
  })]
105
- }), icon && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
106
- className: iconClasses,
107
- style: {
108
- backgroundColor: icon.backgroundColor,
109
- color: icon.fill
110
- },
111
- children: icon.node
112
- })]
123
+ }), renderedIcon]
113
124
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.__experimentalHStack, {
114
125
  alignment: "edge",
115
126
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.__experimentalHStack, {
@@ -117,11 +128,12 @@ var CardFeature = function CardFeature(_ref) {
117
128
  spacing: "8px",
118
129
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_button["default"], {
119
130
  variant: isConfigureState ? 'tertiary' : 'secondary',
120
- disabled: isMuted && !requirementsActionable,
131
+ disabled: isMuted && !requirementsActionable || busy,
132
+ isBusy: busy,
121
133
  onClick: handleButtonClick,
122
134
  size: "compact",
123
135
  children: buttonLabel
124
- }), isConfigureState && !!(moreControls !== null && moreControls !== void 0 && moreControls.length) && /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.DropdownMenu, {
136
+ }), showMoreControls && /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.DropdownMenu, {
125
137
  icon: _icons.moreVertical,
126
138
  label: (0, _i18n.__)('More', 'newspack-plugin'),
127
139
  controls: moreControls,
@@ -30,6 +30,8 @@ var CardSettingsGroup = function CardSettingsGroup(_ref) {
30
30
  disabled = _ref$disabled === void 0 ? false : _ref$disabled,
31
31
  _ref$icon = _ref.icon,
32
32
  icon = _ref$icon === void 0 ? null : _ref$icon,
33
+ _ref$iconElement = _ref.iconElement,
34
+ iconElement = _ref$iconElement === void 0 ? null : _ref$iconElement,
33
35
  headerAction = _ref.headerAction,
34
36
  _ref$title = _ref.title,
35
37
  title = _ref$title === void 0 ? '' : _ref$title,
@@ -58,6 +60,7 @@ var CardSettingsGroup = function CardSettingsGroup(_ref) {
58
60
  onToggle: onEnable,
59
61
  disabled: disabled,
60
62
  icon: icon,
63
+ iconElement: iconElement,
61
64
  iconBackgroundColor: true,
62
65
  isActive: isActive,
63
66
  title: title
@@ -13,7 +13,7 @@ var _element = require("@wordpress/element");
13
13
  var _router = _interopRequireDefault(require("../proxied-imports/router"));
14
14
  var _classnames = _interopRequireDefault(require("classnames"));
15
15
  var _jsxRuntime = require("react/jsx-runtime");
16
- var _excluded = ["className", "size", "hideTitle", "isDestructive", "onConfirm", "onCancel", "when", "isOpen"];
16
+ var _excluded = ["className", "size", "hideTitle", "isDestructive", "onConfirm", "onCancel", "when", "isOpen", "children"];
17
17
  /**
18
18
  * Modal
19
19
  */
@@ -59,6 +59,7 @@ function ConfirmDialog(_ref, ref) {
59
59
  when = _ref$when === void 0 ? false : _ref$when,
60
60
  _ref$isOpen = _ref.isOpen,
61
61
  isOpen = _ref$isOpen === void 0 ? false : _ref$isOpen,
62
+ children = _ref.children,
62
63
  otherProps = (0, _objectWithoutProperties2["default"])(_ref, _excluded);
63
64
  var _useState = (0, _element.useState)(isOpen),
64
65
  _useState2 = (0, _slicedToArray2["default"])(_useState, 2),
@@ -66,6 +67,9 @@ function ConfirmDialog(_ref, ref) {
66
67
  setShowDialog = _useState2[1];
67
68
  var history = useHistory();
68
69
  var pendingNavigation = (0, _element.useRef)(null);
70
+ // While true, the blocker lets navigation through — used so the confirmed
71
+ // ("Discard changes") navigation isn't caught by the still-active blocker.
72
+ var bypassBlock = (0, _element.useRef)(false);
69
73
  var handleOnConfirm = (0, _element.useCallback)(function () {
70
74
  var _pendingNavigation$cu;
71
75
  setShowDialog(false);
@@ -76,8 +80,16 @@ function ConfirmDialog(_ref, ref) {
76
80
  var handleOnCancel = (0, _element.useCallback)(function () {
77
81
  setShowDialog(false);
78
82
  pendingNavigation.current = null;
83
+ // A POP may have moved the URL to the target before it was blocked; put
84
+ // it back so the address bar matches the page the user chose to stay on.
85
+ bypassBlock.current = true;
86
+ try {
87
+ history.replace(history.location);
88
+ } finally {
89
+ bypassBlock.current = false;
90
+ }
79
91
  onCancel();
80
- }, [onCancel, pendingNavigation]);
92
+ }, [onCancel, history]);
81
93
 
82
94
  // Block navigation when there are unsaved changes.
83
95
  (0, _element.useEffect)(function () {
@@ -85,12 +97,25 @@ function ConfirmDialog(_ref, ref) {
85
97
  return;
86
98
  }
87
99
  var unblock = history.block(function (location, action) {
100
+ // Let our own confirmed navigation through instead of re-blocking it.
101
+ if (bypassBlock.current) {
102
+ return undefined;
103
+ }
88
104
  pendingNavigation.current = function () {
89
- unblock();
90
- if (action === 'REPLACE') {
91
- history.replace(location);
92
- } else {
93
- history.push(location);
105
+ bypassBlock.current = true;
106
+ try {
107
+ // A browser/anchor POP (e.g. a breadcrumb or back link) moves the URL
108
+ // to the target before navigation is blocked, and v5 leaves the hash
109
+ // there, so pushing the target would be a no-op. Re-sync to the
110
+ // still-current location first so the real navigation actually fires.
111
+ history.replace(history.location);
112
+ if (action === 'REPLACE') {
113
+ history.replace(location);
114
+ } else {
115
+ history.push(location);
116
+ }
117
+ } finally {
118
+ bypassBlock.current = false;
94
119
  }
95
120
  };
96
121
  setShowDialog(true);
@@ -117,7 +142,8 @@ function ConfirmDialog(_ref, ref) {
117
142
  ref: ref,
118
143
  onConfirm: handleOnConfirm,
119
144
  onCancel: handleOnCancel,
120
- __experimentalHideHeader: false
145
+ __experimentalHideHeader: false,
146
+ children: children
121
147
  }));
122
148
  }
123
149
  var _default = exports["default"] = (0, _element.forwardRef)(ConfirmDialog);
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
5
+ var _react = require("@testing-library/react");
6
+ var _reactRouterDom = require("react-router-dom");
7
+ var _ = _interopRequireDefault(require("./"));
8
+ var _jsxRuntime = require("react/jsx-runtime");
9
+ 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; }
10
+ 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; } /**
11
+ * External dependencies.
12
+ */ /**
13
+ * Internal dependencies.
14
+ */
15
+ var HistoryGrabber = function HistoryGrabber(_ref) {
16
+ var historyRef = _ref.historyRef;
17
+ historyRef.current = (0, _reactRouterDom.useHistory)();
18
+ return null;
19
+ };
20
+ var renderWithHistory = function renderWithHistory() {
21
+ var routerProps = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
22
+ var historyRef = {
23
+ current: null
24
+ };
25
+ (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactRouterDom.MemoryRouter, _objectSpread(_objectSpread({}, routerProps), {}, {
26
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(HistoryGrabber, {
27
+ historyRef: historyRef
28
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
29
+ when: true,
30
+ confirmButtonText: "Discard changes",
31
+ cancelButtonText: "Keep editing",
32
+ children: "Unsaved changes"
33
+ })]
34
+ })));
35
+ return historyRef.current;
36
+ };
37
+
38
+ // The modal's X close button reuses the cancel label as its aria-label, so
39
+ // query by visible text to hit the actual footer buttons.
40
+ var confirmNavigation = function confirmNavigation() {
41
+ return _react.fireEvent.click(_react.screen.getByText('Discard changes', {
42
+ selector: 'button'
43
+ }));
44
+ };
45
+ var cancelNavigation = function cancelNavigation() {
46
+ return _react.fireEvent.click(_react.screen.getByText('Keep editing', {
47
+ selector: 'button'
48
+ }));
49
+ };
50
+ var dialog = function dialog() {
51
+ return _react.screen.queryByText('Unsaved changes');
52
+ };
53
+ describe('ConfirmDialog navigation blocking', function () {
54
+ it('blocks navigation and stays on the current page on cancel', function () {
55
+ var history = renderWithHistory();
56
+ (0, _react.act)(function () {
57
+ return history.push('/next');
58
+ });
59
+ expect(dialog()).toBeInTheDocument();
60
+ expect(history.location.pathname).toBe('/');
61
+ var replaceSpy = jest.spyOn(history, 'replace');
62
+ cancelNavigation();
63
+ expect(dialog()).not.toBeInTheDocument();
64
+ // The URL is re-synced to the page the user chose to stay on.
65
+ expect(replaceSpy).toHaveBeenCalledTimes(1);
66
+ expect(history.location.pathname).toBe('/');
67
+ });
68
+ it('replays a blocked push on confirm: re-sync replace first, then push, and re-arms the blocker', function () {
69
+ var history = renderWithHistory();
70
+ var replaceSpy = jest.spyOn(history, 'replace');
71
+ var pushSpy = jest.spyOn(history, 'push');
72
+ (0, _react.act)(function () {
73
+ return history.push('/next');
74
+ });
75
+ expect(history.location.pathname).toBe('/');
76
+ confirmNavigation();
77
+ expect(history.location.pathname).toBe('/next');
78
+
79
+ // Replay order: re-sync replace fires before the target push.
80
+ var resyncOrder = replaceSpy.mock.invocationCallOrder[0];
81
+ var replayOrder = pushSpy.mock.invocationCallOrder[1];
82
+ expect(replaceSpy).toHaveBeenCalledTimes(1);
83
+ expect(pushSpy).toHaveBeenCalledTimes(2);
84
+ expect(resyncOrder).toBeLessThan(replayOrder);
85
+
86
+ // The bypass only lets the confirmed navigation through; the next one is blocked again.
87
+ (0, _react.act)(function () {
88
+ return history.push('/another');
89
+ });
90
+ expect(history.location.pathname).toBe('/next');
91
+ expect(dialog()).toBeInTheDocument();
92
+ });
93
+ it('replays a blocked REPLACE with replace, not push', function () {
94
+ var history = renderWithHistory();
95
+ var pushSpy = jest.spyOn(history, 'push');
96
+ (0, _react.act)(function () {
97
+ return history.replace('/replaced');
98
+ });
99
+ expect(dialog()).toBeInTheDocument();
100
+ expect(history.location.pathname).toBe('/');
101
+ confirmNavigation();
102
+ expect(history.location.pathname).toBe('/replaced');
103
+ expect(pushSpy).not.toHaveBeenCalled();
104
+ expect(history.length).toBe(1);
105
+ });
106
+ it('replays a blocked POP (back navigation) as a push after the re-sync', function () {
107
+ var history = renderWithHistory({
108
+ initialEntries: ['/start', '/current'],
109
+ initialIndex: 1
110
+ });
111
+ (0, _react.act)(function () {
112
+ return history.goBack();
113
+ });
114
+ expect(dialog()).toBeInTheDocument();
115
+ expect(history.location.pathname).toBe('/current');
116
+ confirmNavigation();
117
+ expect(history.location.pathname).toBe('/start');
118
+ // Pushed rather than popped: the target becomes a new forward entry.
119
+ expect(history.length).toBe(3);
120
+ });
121
+
122
+ // React re-reports errors thrown in event handlers as uncaught window
123
+ // errors; swallow them so the intentional throws don't fail the test.
124
+ var withNavigationError = function withNavigationError(fn) {
125
+ var consoleError = jest.spyOn(console, 'error').mockImplementation(function () {});
126
+ var onWindowError = function onWindowError(event) {
127
+ return event.preventDefault();
128
+ };
129
+ window.addEventListener('error', onWindowError);
130
+ try {
131
+ fn();
132
+ } finally {
133
+ window.removeEventListener('error', onWindowError);
134
+ consoleError.mockRestore();
135
+ }
136
+ };
137
+ it('keeps blocking after a confirmed navigation throws', function () {
138
+ var history = renderWithHistory();
139
+ (0, _react.act)(function () {
140
+ return history.push('/next');
141
+ });
142
+ jest.spyOn(history, 'push').mockImplementationOnce(function () {
143
+ throw new Error('nav failed');
144
+ });
145
+ withNavigationError(function () {
146
+ return confirmNavigation();
147
+ });
148
+
149
+ // bypassBlock was reset by the finally, so navigation is still protected.
150
+ (0, _react.act)(function () {
151
+ return history.push('/elsewhere');
152
+ });
153
+ expect(history.location.pathname).toBe('/');
154
+ expect(dialog()).toBeInTheDocument();
155
+ });
156
+ it('keeps blocking after a cancel URL re-sync throws', function () {
157
+ var history = renderWithHistory();
158
+ (0, _react.act)(function () {
159
+ return history.push('/next');
160
+ });
161
+ jest.spyOn(history, 'replace').mockImplementationOnce(function () {
162
+ throw new Error('replace failed');
163
+ });
164
+ withNavigationError(function () {
165
+ return cancelNavigation();
166
+ });
167
+ (0, _react.act)(function () {
168
+ return history.push('/elsewhere');
169
+ });
170
+ expect(history.location.pathname).toBe('/');
171
+ expect(dialog()).toBeInTheDocument();
172
+ });
173
+ });
@@ -34,7 +34,7 @@
34
34
  // Variant
35
35
 
36
36
  &--variant-default {
37
- background-color: wp-colors.$gray-300;
37
+ background-color: var( --wpds-color-stroke-surface-neutral, #{wp-colors.$gray-300} );
38
38
  }
39
39
 
40
40
  &--variant-primary {
@@ -46,6 +46,8 @@
46
46
  }
47
47
 
48
48
  &--variant-tertiary {
49
- background-color: wp-colors.$gray-100;
49
+ // Fallback for consumers whose bundle doesn't carry the wpds token sheet
50
+ // (it ships with the Page header's stylesheet).
51
+ background-color: var( --wpds-color-stroke-surface-neutral-weak, #{wp-colors.$gray-100} );
50
52
  }
51
53
  }
@@ -61,6 +61,11 @@
61
61
  grid-row-gap: 16px;
62
62
  }
63
63
 
64
+ // Row Gap 24
65
+ &__row-gap-24 {
66
+ grid-row-gap: 24px;
67
+ }
68
+
64
69
  // Row Gap 32
65
70
  &__row-gap-32 {
66
71
  grid-row-gap: 32px;
@@ -14,6 +14,7 @@ var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits
14
14
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
15
15
  var _apiFetch = _interopRequireDefault(require("@wordpress/api-fetch"));
16
16
  var _element = require("@wordpress/element");
17
+ var _components = require("@wordpress/components");
17
18
  var _i18n = require("@wordpress/i18n");
18
19
  var _ = require("../");
19
20
  var _assign = _interopRequireDefault(require("lodash/assign"));
@@ -27,7 +28,8 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
27
28
  * Handoff
28
29
  */ /**
29
30
  * WordPress dependencies.
30
- */ /**
31
+ */ // eslint-disable-line @wordpress/no-unsafe-wp-apis
32
+ /**
31
33
  * Internal dependencies.
32
34
  */ /**
33
35
  * External dependencies.
@@ -202,10 +204,11 @@ var Handoff = /*#__PURE__*/function (_Component) {
202
204
  },
203
205
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("p", {
204
206
  children: modalBody
205
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_.Card, {
206
- buttonsCard: true,
207
- noBorder: true,
208
- className: "justify-end",
207
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.__experimentalHStack, {
208
+ justify: "flex-end",
209
+ spacing: 4,
210
+ wrap: true,
211
+ className: "newspack-modal__footer",
209
212
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_.Button, {
210
213
  variant: "secondary",
211
214
  onClick: function onClick() {
package/dist/cjs/index.js CHANGED
@@ -10,6 +10,12 @@ Object.defineProperty(exports, "Accordion", {
10
10
  return _accordion["default"];
11
11
  }
12
12
  });
13
+ Object.defineProperty(exports, "AccordionPanel", {
14
+ enumerable: true,
15
+ get: function get() {
16
+ return _accordion.AccordionPanel;
17
+ }
18
+ });
13
19
  Object.defineProperty(exports, "ActionCard", {
14
20
  enumerable: true,
15
21
  get: function get() {
@@ -46,6 +52,12 @@ Object.defineProperty(exports, "BoxContrast", {
46
52
  return _boxContrast["default"];
47
53
  }
48
54
  });
55
+ Object.defineProperty(exports, "Breadcrumbs", {
56
+ enumerable: true,
57
+ get: function get() {
58
+ return _breadcrumbs["default"];
59
+ }
60
+ });
49
61
  Object.defineProperty(exports, "Button", {
50
62
  enumerable: true,
51
63
  get: function get() {
@@ -166,6 +178,12 @@ Object.defineProperty(exports, "InfoButton", {
166
178
  return _infoButton["default"];
167
179
  }
168
180
  });
181
+ Object.defineProperty(exports, "IntegrationIcon", {
182
+ enumerable: true,
183
+ get: function get() {
184
+ return _integrationIcon["default"];
185
+ }
186
+ });
169
187
  Object.defineProperty(exports, "Modal", {
170
188
  enumerable: true,
171
189
  get: function get() {
@@ -184,6 +202,12 @@ Object.defineProperty(exports, "Notice", {
184
202
  return _notice["default"];
185
203
  }
186
204
  });
205
+ Object.defineProperty(exports, "Page", {
206
+ enumerable: true,
207
+ get: function get() {
208
+ return _page["default"];
209
+ }
210
+ });
187
211
  Object.defineProperty(exports, "PluginInstaller", {
188
212
  enumerable: true,
189
213
  get: function get() {
@@ -292,6 +316,12 @@ Object.defineProperty(exports, "Wizard", {
292
316
  return _wizard["default"];
293
317
  }
294
318
  });
319
+ Object.defineProperty(exports, "espProviderOrder", {
320
+ enumerable: true,
321
+ get: function get() {
322
+ return integrationIcons.espProviderOrder;
323
+ }
324
+ });
295
325
  Object.defineProperty(exports, "hooks", {
296
326
  enumerable: true,
297
327
  get: function get() {
@@ -329,12 +359,13 @@ Object.defineProperty(exports, "withWizardScreen", {
329
359
  return _withWizardScreen["default"];
330
360
  }
331
361
  });
332
- var _accordion = _interopRequireDefault(require("./accordion"));
362
+ var _accordion = _interopRequireWildcard(require("./accordion"));
333
363
  var _actionCard = _interopRequireDefault(require("./action-card"));
334
364
  var _autocompleteTokenfield = _interopRequireDefault(require("./autocomplete-tokenfield"));
335
365
  var _autocompleteWithSuggestions = _interopRequireDefault(require("./autocomplete-with-suggestions"));
336
366
  var _autocompleteWithLatestPosts = _interopRequireDefault(require("./autocomplete-with-latest-posts"));
337
367
  var _badge = _interopRequireDefault(require("./badge"));
368
+ var _breadcrumbs = _interopRequireDefault(require("./breadcrumbs"));
338
369
  var _button = _interopRequireDefault(require("./button"));
339
370
  var _boxContrast = _interopRequireDefault(require("./box-contrast"));
340
371
  var _card = _interopRequireDefault(require("./card"));
@@ -342,6 +373,7 @@ var _cardFeature = _interopRequireDefault(require("./card-feature"));
342
373
  var _cardForm = _interopRequireDefault(require("./card-form"));
343
374
  var _cardSettingsGroup = _interopRequireDefault(require("./card-settings-group"));
344
375
  var _cardSortableList = _interopRequireDefault(require("./card-sortable-list"));
376
+ var _integrationIcon = _interopRequireDefault(require("./integration-icon"));
345
377
  var _categoryAutocomplete = _interopRequireDefault(require("./category-autocomplete"));
346
378
  var _colorPicker = _interopRequireDefault(require("./color-picker"));
347
379
  var _confirmDialog = _interopRequireDefault(require("./confirm-dialog"));
@@ -359,6 +391,7 @@ var _grid = _interopRequireDefault(require("./grid"));
359
391
  var _modal = _interopRequireDefault(require("./modal"));
360
392
  var _newspackIcon = _interopRequireDefault(require("./newspack-icon"));
361
393
  var _notice = _interopRequireDefault(require("./notice"));
394
+ var _page = _interopRequireDefault(require("./page"));
362
395
  var _pluginInstaller = _interopRequireDefault(require("./plugin-installer"));
363
396
  var _pluginSettings = _interopRequireDefault(require("./plugin-settings"));
364
397
  var _pluginToggle = _interopRequireDefault(require("./plugin-toggle"));
@@ -0,0 +1,40 @@
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 _classnames = _interopRequireDefault(require("classnames"));
9
+ var _integrationIcons = require("../integration-icons");
10
+ require("./style.scss");
11
+ var _jsxRuntime = require("react/jsx-runtime");
12
+ /**
13
+ * External dependencies
14
+ */
15
+
16
+ /**
17
+ * Internal dependencies
18
+ */
19
+
20
+ /**
21
+ * The single source for how an ESP provider is shown: brand mark plus badge
22
+ * background. A new provider needs an icon file, an `espProviderIcons` entry,
23
+ * and a background rule in this component's stylesheet.
24
+ */var IntegrationIcon = function IntegrationIcon(_ref) {
25
+ var provider = _ref.provider,
26
+ className = _ref.className;
27
+ var icons = _integrationIcons.espProviderIcons;
28
+ var node = Object.prototype.hasOwnProperty.call(_integrationIcons.espProviderIcons, provider) ? icons[provider] : null;
29
+ if (!node) {
30
+ return null;
31
+ }
32
+ // Decorative: the provider is always named in adjacent visible text (the card
33
+ // title), so hide the mark from assistive tech to avoid duplicate announcements.
34
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
35
+ "aria-hidden": "true",
36
+ className: (0, _classnames["default"])('newspack-integration-icon', "newspack-integration-icon--".concat(provider.replace(/_/g, '-')), className),
37
+ children: node
38
+ });
39
+ };
40
+ var _default = exports["default"] = IntegrationIcon;
@@ -0,0 +1,42 @@
1
+ @use "~@wordpress/base-styles/colors" as wp-colors;
2
+ @use "~@wordpress/base-styles/variables" as wp-vars;
3
+
4
+ .newspack-integration-icon {
5
+ align-items: center;
6
+ border-radius: wp-vars.$radius-round;
7
+ display: flex;
8
+ flex: 0 0 wp-vars.$grid-unit-50;
9
+ height: wp-vars.$grid-unit-50;
10
+ justify-content: center;
11
+ width: wp-vars.$grid-unit-50;
12
+
13
+ svg {
14
+ display: block;
15
+ fill: currentcolor;
16
+ height: wp-vars.$grid-unit-30;
17
+ width: wp-vars.$grid-unit-30;
18
+ }
19
+
20
+ &--active-campaign {
21
+ background: #004cff;
22
+ }
23
+
24
+ &--mailchimp {
25
+ background: #ffe01b;
26
+ }
27
+
28
+ &--constant-contact {
29
+ background: wp-colors.$gray-100;
30
+ }
31
+
32
+ &--manual {
33
+ background: wp-colors.$gray-800;
34
+ color: wp-colors.$white;
35
+
36
+ // Set the fill on the path directly so the card's active/hover state
37
+ // (which recolours header svgs) can't override the white manual mark.
38
+ svg path {
39
+ fill: currentcolor;
40
+ }
41
+ }
42
+ }
@@ -16,6 +16,7 @@ Object.defineProperty(exports, "constantContact", {
16
16
  return _constantContact["default"];
17
17
  }
18
18
  });
19
+ exports.espProviderOrder = exports.espProviderIcons = void 0;
19
20
  Object.defineProperty(exports, "fundraiseUp", {
20
21
  enumerable: true,
21
22
  get: function get() {
@@ -40,9 +41,27 @@ Object.defineProperty(exports, "wisepops", {
40
41
  return _wisepops["default"];
41
42
  }
42
43
  });
44
+ var _icons = require("@wordpress/icons");
43
45
  var _activeCampaign = _interopRequireDefault(require("./active-campaign"));
44
46
  var _constantContact = _interopRequireDefault(require("./constant-contact"));
45
- var _fundraiseUp = _interopRequireDefault(require("./fundraise-up"));
46
47
  var _mailchimp = _interopRequireDefault(require("./mailchimp"));
48
+ var _fundraiseUp = _interopRequireDefault(require("./fundraise-up"));
47
49
  var _salesforce = _interopRequireDefault(require("./salesforce"));
48
- var _wisepops = _interopRequireDefault(require("./wisepops"));
50
+ var _wisepops = _interopRequireDefault(require("./wisepops"));
51
+ /**
52
+ * WordPress dependencies
53
+ */
54
+
55
+ /**
56
+ * Internal dependencies
57
+ */
58
+
59
+ // ESP provider brand marks keyed by the slug the backend reports
60
+ // (`Newspack_Newsletters::service_provider()`). Rendered via IntegrationIcon.
61
+ var espProviderIcons = exports.espProviderIcons = {
62
+ active_campaign: _activeCampaign["default"],
63
+ mailchimp: _mailchimp["default"],
64
+ constant_contact: _constantContact["default"],
65
+ manual: _icons.atSymbol
66
+ };
67
+ var espProviderOrder = exports.espProviderOrder = ['active_campaign', 'mailchimp', 'constant_contact', 'manual'];
@@ -99,3 +99,7 @@
99
99
  .modal-open .components-popover__content {
100
100
  opacity: 0;
101
101
  }
102
+
103
+ .newspack-modal__footer {
104
+ margin-top: 32px;
105
+ }