newspack-components 4.8.0-alpha.1 → 4.8.1-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.
- package/CHANGELOG.md +10 -1
- package/DEVELOPMENT.md +18 -17
- package/dist/cjs/action-card/index.js +85 -18
- package/dist/cjs/action-card/index.test.js +192 -5
- package/dist/cjs/action-card/style.scss +3 -9
- package/dist/cjs/debug-badge/index.js +44 -0
- package/dist/cjs/debug-badge/index.test.js +50 -0
- package/dist/cjs/debug-badge/style.scss +26 -0
- package/dist/cjs/empty-state/index.test.js +37 -24
- package/dist/cjs/empty-state/root.js +2 -1
- package/dist/cjs/footer/index.js +52 -16
- package/dist/cjs/footer/index.test.js +208 -0
- package/dist/cjs/footer/style.scss +17 -1
- package/dist/cjs/index.js +7 -0
- package/dist/cjs/notice/index.js +1 -4
- package/dist/cjs/notice/style.scss +0 -19
- package/dist/cjs/plugin-toggle/index.js +1 -1
- package/dist/cjs/with-wizard-screen/index.js +1 -3
- package/dist/cjs/with-wizard-screen/index.test.js +0 -3
- package/dist/cjs/wizard/index.js +8 -7
- package/dist/cjs/wizard/index.test.js +158 -5
- package/dist/cjs/wizard/store/index.js +2 -0
- package/dist/esm/action-card/index.js +86 -21
- package/dist/esm/action-card/index.test.js +192 -1
- package/dist/esm/action-card/style.scss +3 -9
- package/dist/esm/debug-badge/index.js +39 -0
- package/dist/esm/debug-badge/index.test.js +46 -0
- package/dist/esm/debug-badge/style.scss +26 -0
- package/dist/esm/empty-state/index.test.js +37 -24
- package/dist/esm/empty-state/root.js +2 -1
- package/dist/esm/footer/index.js +52 -17
- package/dist/esm/footer/index.test.js +210 -0
- package/dist/esm/footer/style.scss +17 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/notice/index.js +2 -5
- package/dist/esm/notice/style.scss +0 -19
- package/dist/esm/plugin-toggle/index.js +1 -1
- package/dist/esm/with-wizard-screen/index.js +2 -4
- package/dist/esm/with-wizard-screen/index.test.js +0 -3
- package/dist/esm/wizard/index.js +11 -10
- package/dist/esm/wizard/index.test.js +158 -5
- package/dist/esm/wizard/store/index.js +2 -0
- package/package.json +1 -1
- package/src/action-card/action-card.d.ts +3 -2
- package/src/action-card/index.js +81 -8
- package/src/action-card/index.test.js +159 -0
- package/src/action-card/style.scss +3 -9
- package/src/debug-badge/index.js +36 -0
- package/src/debug-badge/index.test.js +39 -0
- package/src/debug-badge/style.scss +26 -0
- package/src/empty-state/README.md +11 -10
- package/src/empty-state/index.test.js +10 -0
- package/src/empty-state/root.tsx +2 -1
- package/src/empty-state/types.ts +1 -1
- package/src/footer/index.js +47 -2
- package/src/footer/index.test.js +114 -0
- package/src/footer/style.scss +17 -1
- package/src/index.js +1 -0
- package/src/notice/index.js +2 -17
- package/src/notice/style.scss +0 -19
- package/src/plugin-toggle/index.js +1 -1
- package/src/with-wizard-screen/index.js +2 -2
- package/src/with-wizard-screen/index.test.js +0 -2
- package/src/wizard/index.js +11 -9
- package/src/wizard/index.test.js +96 -3
- package/src/wizard/store/index.js +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
## newspack-components [4.8.1-alpha.1](https://github.com/Automattic/newspack-workspace/compare/newspack-components@4.8.0...newspack-components@4.8.1-alpha.1) (2026-09-17)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **components:** confirm before the footer's destructive actions ([#965](https://github.com/Automattic/newspack-workspace/issues/965)) ([e4e6676](https://github.com/Automattic/newspack-workspace/commit/e4e66763aa5ce74265689aa6fee4941f2f7348be))
|
|
7
|
+
* **empty-state:** adopt the shared component on three screens ([#1035](https://github.com/Automattic/newspack-workspace/issues/1035)) ([ddfc071](https://github.com/Automattic/newspack-workspace/commit/ddfc071f905e37b20addce7a6416f22665c9042d))
|
|
8
|
+
* **subscribers:** use core Notice for load-failure states ([#1029](https://github.com/Automattic/newspack-workspace/issues/1029)) ([f13281b](https://github.com/Automattic/newspack-workspace/commit/f13281b9d5a176d8beb9667aba6f8d599c6ad331))
|
|
9
|
+
|
|
10
|
+
# newspack-components [4.8.0](https://github.com/Automattic/newspack-workspace/compare/newspack-components@4.7.0...newspack-components@4.8.0) (2026-09-14)
|
|
2
11
|
|
|
3
12
|
|
|
4
13
|
### Bug Fixes
|
package/DEVELOPMENT.md
CHANGED
|
@@ -100,7 +100,7 @@ When building a screen, use the **spacing scale** (8px unit: 16, 24, 32, 48, 64)
|
|
|
100
100
|
|
|
101
101
|
- **`ActionCard`** – Use when one concept (e.g. a feature or setting) can be toggled on/off and may have extra content below. Internal padding (24px default; 16px/8px for isMedium/isSmall) and 24px between regions keep hierarchy clear; expandable content uses 24px top padding and 24px between siblings.
|
|
102
102
|
- **`CollapsibleGroup`** / **`CollapsibleGroup.Item`** – A stack of independently collapsible items, separated by dividers and flush with the surrounding column. Not a W3C accordion: the items do not coordinate.
|
|
103
|
-
- **`Notice`** –
|
|
103
|
+
- **`Notice`** – Being retired in favour of core’s `Notice` from `@wordpress/components`, which announces itself to assistive technology. Reach for core’s in new work; ours stays for the screens still on it. Where ours is used, vertical margin is 32px so notices don’t collide with cards, and one primary message per area reads best.
|
|
104
104
|
- **`ProgressBar`** – Progress indicator.
|
|
105
105
|
- **`StepsList`** / **`StepsListItem`** – Step-by-step list components.
|
|
106
106
|
- **`StyleCard`** – Style preview card.
|
|
@@ -131,6 +131,7 @@ When building a screen, use the **spacing scale** (8px unit: 16, 24, 32, 48, 64)
|
|
|
131
131
|
|
|
132
132
|
### Utility Components
|
|
133
133
|
|
|
134
|
+
- **`DebugBadge`** - Floating indicator that Newspack debug mode is on. Takes no props and renders nothing unless the site defines `WP_NEWSPACK_DEBUG`
|
|
134
135
|
- **`GlobalNotices`** - Global notice system component
|
|
135
136
|
- **`InfoButton`** - Reveals supplementary context from a `description` prop. Anything a reader needs in order to use a control belongs in visible help text instead
|
|
136
137
|
- **`Modal`** - Modal dialog component
|
|
@@ -176,7 +177,7 @@ import {
|
|
|
176
177
|
ActionCard,
|
|
177
178
|
Button,
|
|
178
179
|
Card,
|
|
179
|
-
|
|
180
|
+
SectionHeader,
|
|
180
181
|
TextControl,
|
|
181
182
|
} from '../../../../../packages/components/src';
|
|
182
183
|
```
|
|
@@ -187,21 +188,21 @@ import {
|
|
|
187
188
|
|
|
188
189
|
```jsx
|
|
189
190
|
// ✅ CORRECT - Within newspack-plugin monorepo
|
|
190
|
-
import { Button, Card,
|
|
191
|
+
import { Button, Card, SectionHeader } from '../../../../../packages/components/src';
|
|
191
192
|
```
|
|
192
193
|
|
|
193
194
|
**As an npm package:** If `newspack-components` is installed as a dependency in another plugin, import from the package name:
|
|
194
195
|
|
|
195
196
|
```jsx
|
|
196
197
|
// ✅ CORRECT - When installed as npm package
|
|
197
|
-
import { Button, Card,
|
|
198
|
+
import { Button, Card, SectionHeader } from 'newspack-components';
|
|
198
199
|
```
|
|
199
200
|
|
|
200
201
|
**Import individual components** – Import only what you need; do not import the whole namespace. List named imports **alphabetically** (e.g. from `@wordpress/components` or `newspack-components`):
|
|
201
202
|
|
|
202
203
|
```jsx
|
|
203
204
|
// ✅ CORRECT – alphabetical
|
|
204
|
-
import { Button, Card,
|
|
205
|
+
import { Button, Card, SectionHeader } from '../../../../../packages/components/src';
|
|
205
206
|
|
|
206
207
|
// ❌ WRONG – Don't import all
|
|
207
208
|
import * as Components from '../../../../../packages/components/src';
|
|
@@ -253,7 +254,7 @@ This section shows how to use components **by context** (backend, blocks, fronte
|
|
|
253
254
|
/**
|
|
254
255
|
* WordPress dependencies
|
|
255
256
|
*/
|
|
256
|
-
import { CheckboxControl, ExternalLink, RangeControl } from '@wordpress/components';
|
|
257
|
+
import { CheckboxControl, ExternalLink, Notice, RangeControl } from '@wordpress/components';
|
|
257
258
|
|
|
258
259
|
/**
|
|
259
260
|
* Internal dependencies
|
|
@@ -262,9 +263,8 @@ import {
|
|
|
262
263
|
ActionCard,
|
|
263
264
|
Button,
|
|
264
265
|
Card,
|
|
265
|
-
Grid,
|
|
266
|
-
Notice,
|
|
267
266
|
Divider,
|
|
267
|
+
Grid,
|
|
268
268
|
SectionHeader,
|
|
269
269
|
TextControl,
|
|
270
270
|
Waiting,
|
|
@@ -273,14 +273,13 @@ import {
|
|
|
273
273
|
|
|
274
274
|
**Example – Audience Setup Wizard (real reference):**
|
|
275
275
|
```jsx
|
|
276
|
-
import { CheckboxControl, ExternalLink, RangeControl } from '@wordpress/components';
|
|
276
|
+
import { CheckboxControl, ExternalLink, Notice, RangeControl } from '@wordpress/components';
|
|
277
277
|
import {
|
|
278
278
|
ActionCard,
|
|
279
279
|
Button,
|
|
280
280
|
Card,
|
|
281
|
-
Grid,
|
|
282
|
-
Notice,
|
|
283
281
|
Divider,
|
|
282
|
+
Grid,
|
|
284
283
|
PluginInstaller,
|
|
285
284
|
SectionHeader,
|
|
286
285
|
TextControl,
|
|
@@ -291,7 +290,9 @@ import {
|
|
|
291
290
|
export default withWizardScreen( ( { config, updateConfig } ) => {
|
|
292
291
|
return (
|
|
293
292
|
<>
|
|
294
|
-
<Notice
|
|
293
|
+
<Notice status="success" isDismissible={ false }>
|
|
294
|
+
{ __( 'Audience Management is enabled.', 'newspack-plugin' ) }
|
|
295
|
+
</Notice>
|
|
295
296
|
<Card noBorder>
|
|
296
297
|
<ActionCard
|
|
297
298
|
title={ __( 'Present newsletter signup after checkout', 'newspack-plugin' ) }
|
|
@@ -374,7 +375,7 @@ import { Fragment } from '@wordpress/element';
|
|
|
374
375
|
/**
|
|
375
376
|
* Internal dependencies
|
|
376
377
|
*/
|
|
377
|
-
import { GlobalNotices,
|
|
378
|
+
import { GlobalNotices, Wizard } from '../../../../../packages/components/src';
|
|
378
379
|
import sections from './sections';
|
|
379
380
|
|
|
380
381
|
function Dashboard() {
|
|
@@ -541,7 +542,7 @@ When Newspack components don't provide what you need, use these WordPress compon
|
|
|
541
542
|
### Feedback and overlays
|
|
542
543
|
|
|
543
544
|
- **`Spinner`** – Loading spinner
|
|
544
|
-
- **`Notice`** – Inline notice (success/error/warning); prefer Newspack `Notice
|
|
545
|
+
- **`Notice`** – Inline notice (success/error/warning); prefer this over the Newspack `Notice`, which is being retired
|
|
545
546
|
- **`Placeholder`** – Empty state in blocks
|
|
546
547
|
- **`Modal`** – Modal dialog
|
|
547
548
|
- **`Popover`** – Popover (e.g. webhooks endpoint actions, corrections modal)
|
|
@@ -622,7 +623,7 @@ Spacing is based on an **8px unit**. Use these values so new styles match existi
|
|
|
622
623
|
| **8px** | Tight gaps, badge padding, small padding (e.g. ActionCard is-small) | Inline or dense UI |
|
|
623
624
|
| **16px** | Gaps between related controls, buttons card gap, margins inside ActionCard region-children for Card/Grid/TextControl | Related items, form rows |
|
|
624
625
|
| **24px** | Default ActionCard region padding, toggle/region gaps, expandable content padding and sibling spacing | Default internal padding and gaps within a card |
|
|
625
|
-
| **32px** | Card vertical margin, Grid default gap and margin, SectionHeader first-child top, Notice margin, Divider margin | Section rhythm, between blocks |
|
|
626
|
+
| **32px** | Card vertical margin, Grid default gap and margin, SectionHeader first-child top, Newspack Notice margin, Divider margin | Section rhythm, between blocks |
|
|
626
627
|
| **48px** | SectionHeader container margin-top, Card horizontal padding (small screens) | Section separation |
|
|
627
628
|
| **64px** | SectionHeader top margin, Divider margins (large breakpoint), buttons card margin, Card horizontal padding (large screens) | Major section separation |
|
|
628
629
|
|
|
@@ -693,7 +694,7 @@ Components rely on WordPress design system states where applicable; a few Newspa
|
|
|
693
694
|
- **ActionCard (clickable)** – Hover: `box-shadow: 0 4px 8px rgba(black, 0.08)`; transition 125ms ease-in-out. Use for cards that navigate or open.
|
|
694
695
|
- **Button** – Primary, secondary, disabled, and link variants follow `@wordpress/components` Button; focus and hover come from WordPress base styles.
|
|
695
696
|
- **Toggle (inside ActionCard)** – Checked/unchecked and focus states from WordPress ToggleControl; label is visually hidden but available for accessibility.
|
|
696
|
-
- **Notice** –
|
|
697
|
+
- **Notice** – Core's `Notice` carries the status in a tinted, bordered box (info, success, warning, error) and announces itself. The Newspack one, still in place on the screens that have not moved, tints the background and adds an icon. Either way, use notices for feedback only, and one primary message per area when possible.
|
|
697
698
|
|
|
698
699
|
When adding new interactive components, preserve focus visibility and use the same state patterns (hover shadow, transition) so the UI feels consistent.
|
|
699
700
|
|
|
@@ -724,7 +725,7 @@ When adding new interactive components, preserve focus visibility and use the sa
|
|
|
724
725
|
- **[@wordpress/components](https://www.npmjs.com/package/@wordpress/components)** – WordPress component library (fallback when Newspack components don’t exist)
|
|
725
726
|
- **[@wordpress/base-styles](https://www.npmjs.com/package/@wordpress/base-styles)** – WordPress Design System styles and colors
|
|
726
727
|
|
|
727
|
-
**Design resources:** For layout, components, and spacing, refer to the [WordPress Figma Library](https://www.figma.com/community/file/1149596986784498103/wordpress-design-library) and the [Block Editor Handbook](https://developer.wordpress.org/block-editor/reference-guides/components/). The **Components Demo** (`/wp-admin/admin.php?page=newspack-components-demo`) is the live reference for how Newspack components look and behave; use it to confirm spacing and hierarchy when building new screens.
|
|
728
|
+
**Design resources:** For layout, components, and spacing, refer to the [WordPress Figma Library](https://www.figma.com/community/file/1149596986784498103/wordpress-design-library) and the [Block Editor Handbook](https://developer.wordpress.org/block-editor/reference-guides/components/). The **Components Demo** (`/wp-admin/admin.php?page=newspack-components-demo`) is the live reference for how Newspack components look and behave; use it to confirm spacing and hierarchy when building new screens. It no longer catalogues `Notice`; the Action Card examples are the place to see one rendered.
|
|
728
729
|
|
|
729
730
|
## Component Patterns
|
|
730
731
|
|
|
@@ -9,6 +9,7 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
|
|
|
9
9
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
10
10
|
var _components = require("@wordpress/components");
|
|
11
11
|
var _element = require("@wordpress/element");
|
|
12
|
+
var _htmlEntities = require("@wordpress/html-entities");
|
|
12
13
|
var _i18n = require("@wordpress/i18n");
|
|
13
14
|
var _icons = require("@wordpress/icons");
|
|
14
15
|
var _ui = require("@wordpress/ui");
|
|
@@ -26,8 +27,80 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
26
27
|
* Internal dependencies
|
|
27
28
|
*/ /**
|
|
28
29
|
* External dependencies
|
|
29
|
-
*/
|
|
30
|
+
*/
|
|
31
|
+
var NOTIFICATION_LEVELS = ['error', 'info', 'success', 'warning'];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Collect a notification's text, including text nested inside React elements.
|
|
35
|
+
*
|
|
36
|
+
* Segments join with a space so that adjacent elements do not weld together: by the
|
|
37
|
+
* time speak() sees this string the markup that separated them is already gone.
|
|
38
|
+
*
|
|
39
|
+
* @param {*} value Notification content.
|
|
40
|
+
* @return {string} The collected text.
|
|
41
|
+
*/
|
|
42
|
+
var _getNotificationText = function getNotificationText(value) {
|
|
43
|
+
var _value$props;
|
|
44
|
+
if (typeof value === 'string') {
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
if (typeof value === 'number') {
|
|
48
|
+
return String(value);
|
|
49
|
+
}
|
|
50
|
+
if (value instanceof Error) {
|
|
51
|
+
return value.message;
|
|
52
|
+
}
|
|
53
|
+
if (Array.isArray(value)) {
|
|
54
|
+
return value.map(_getNotificationText).join(' ');
|
|
55
|
+
}
|
|
56
|
+
return (value === null || value === void 0 || (_value$props = value.props) === null || _value$props === void 0 ? void 0 : _value$props.children) === undefined ? '' : _getNotificationText(value.props.children);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Derive a plain-text announcement from a notification.
|
|
61
|
+
*
|
|
62
|
+
* Notice defaults spokenMessage to its children and runs renderToString over them
|
|
63
|
+
* during render, which corrupts the hook dispatcher when those children are
|
|
64
|
+
* components. A notification can be any element, so the announcement is always
|
|
65
|
+
* built from its text instead of letting that default apply. Tags in an HTML
|
|
66
|
+
* notification stay in, since speak() strips them itself. Entities are decoded so
|
|
67
|
+
* the announcement matches the text on screen, which getNotificationContent decodes
|
|
68
|
+
* for the same reason.
|
|
69
|
+
*
|
|
70
|
+
* @param {*} notification Notification content.
|
|
71
|
+
* @return {string} Message to announce.
|
|
72
|
+
*/
|
|
73
|
+
var getSpokenNotification = function getSpokenNotification(notification) {
|
|
74
|
+
return (0, _htmlEntities.decodeEntities)(_getNotificationText(notification)).replace(/\s+/g, ' ').trim();
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Decode a notification's own text for display.
|
|
79
|
+
*
|
|
80
|
+
* React does not decode entities in text children, and notification text is often
|
|
81
|
+
* server-sourced, so a message carrying `’` would otherwise show the entity
|
|
82
|
+
* and announce the character. The HTML branch is left alone: there the browser's
|
|
83
|
+
* parser decodes as it renders.
|
|
84
|
+
*
|
|
85
|
+
* @param {*} value Notification content.
|
|
86
|
+
* @return {*} The content, with its own strings decoded.
|
|
87
|
+
*/
|
|
88
|
+
var _getNotificationContent = function getNotificationContent(value) {
|
|
89
|
+
if (value instanceof Error) {
|
|
90
|
+
return (0, _htmlEntities.decodeEntities)(value.message);
|
|
91
|
+
}
|
|
92
|
+
if (typeof value === 'string') {
|
|
93
|
+
return (0, _htmlEntities.decodeEntities)(value);
|
|
94
|
+
}
|
|
95
|
+
if (Array.isArray(value)) {
|
|
96
|
+
return value.map(_getNotificationContent);
|
|
97
|
+
}
|
|
98
|
+
return value;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
30
102
|
* ActionCard component
|
|
103
|
+
*
|
|
31
104
|
* @param {ActionCardProps} props Component props.
|
|
32
105
|
* @return {JSX.Element} ActionCard component.
|
|
33
106
|
*/
|
|
@@ -124,6 +197,7 @@ var ActionCard = function ActionCard(_ref) {
|
|
|
124
197
|
}
|
|
125
198
|
}, [collapse]);
|
|
126
199
|
var hasChildren = notification || children;
|
|
200
|
+
var notificationContent = _getNotificationContent(notification);
|
|
127
201
|
var classes = (0, _classnames["default"])('newspack-action-card', simple && 'newspack-card--is-clickable', hasGreyHeader && 'newspack-card--has-grey-header', hasWhiteHeader && 'newspack-card--has-white-header', hasChildren && 'newspack-card--has-children', indent && 'newspack-card--indent', isSmall && 'is-small', isMedium && 'is-medium', checkbox && 'has-checkbox', expandable && 'is-expandable', draggable && 'is-draggable', actionContent && 'has-action-content', className);
|
|
128
202
|
var backgroundImageStyles = function backgroundImageStyles(url) {
|
|
129
203
|
return url ? {
|
|
@@ -247,24 +321,17 @@ var ActionCard = function ActionCard(_ref) {
|
|
|
247
321
|
width: 24
|
|
248
322
|
})
|
|
249
323
|
})]
|
|
250
|
-
}), notification && /*#__PURE__*/(0, _jsxRuntime.
|
|
324
|
+
}), notification && NOTIFICATION_LEVELS.includes(notificationLevel) && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
251
325
|
className: "newspack-action-card__notification newspack-action-card__region-children",
|
|
252
|
-
children:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
isSuccess: true,
|
|
262
|
-
rawHTML: notificationHTML
|
|
263
|
-
}), 'warning' === notificationLevel && /*#__PURE__*/(0, _jsxRuntime.jsx)(_.Notice, {
|
|
264
|
-
noticeText: notification,
|
|
265
|
-
isWarning: true,
|
|
266
|
-
rawHTML: notificationHTML
|
|
267
|
-
})]
|
|
326
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Notice, {
|
|
327
|
+
status: notificationLevel,
|
|
328
|
+
isDismissible: false,
|
|
329
|
+
spokenMessage: getSpokenNotification(notification),
|
|
330
|
+
children: notificationHTML && typeof notification === 'string' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_element.RawHTML, {
|
|
331
|
+
className: "newspack-action-card__notification-html",
|
|
332
|
+
children: notification
|
|
333
|
+
}) : notificationContent
|
|
334
|
+
})
|
|
268
335
|
}), children && (expandable && expanded || !expandable) ? /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
269
336
|
className: "newspack-action-card__region-children",
|
|
270
337
|
children: children
|
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
5
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
4
6
|
var _react = require("@testing-library/react");
|
|
7
|
+
var _a11y = require("@wordpress/a11y");
|
|
8
|
+
var _element = require("@wordpress/element");
|
|
5
9
|
var _index = _interopRequireDefault(require("./index"));
|
|
6
10
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
7
|
-
|
|
11
|
+
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; }
|
|
12
|
+
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; } /**
|
|
8
13
|
* External dependencies.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
14
|
+
*/ /**
|
|
15
|
+
* WordPress dependencies.
|
|
16
|
+
*/ /**
|
|
12
17
|
* Internal dependencies.
|
|
13
18
|
*/
|
|
19
|
+
jest.mock('@wordpress/a11y', function () {
|
|
20
|
+
return _objectSpread(_objectSpread({}, jest.requireActual('@wordpress/a11y')), {}, {
|
|
21
|
+
speak: jest.fn()
|
|
22
|
+
});
|
|
23
|
+
});
|
|
14
24
|
|
|
15
25
|
/**
|
|
16
26
|
* The library Badge styles its wrapper rather than its text, so a badge with no label
|
|
@@ -18,7 +28,8 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
18
28
|
* absent — a payment gateway reports no connection status until it is enabled, a prompt
|
|
19
29
|
* can sit on a placement with no entry in the placement map — and an array literal is
|
|
20
30
|
* always non-empty, so the component has to drop those itself.
|
|
21
|
-
*/
|
|
31
|
+
*/
|
|
32
|
+
describe('ActionCard badges', function () {
|
|
22
33
|
var badgeText = function badgeText() {
|
|
23
34
|
return _react.screen.queryAllByText(function (_, node) {
|
|
24
35
|
return /__badge/.test((node === null || node === void 0 ? void 0 : node.className) || '');
|
|
@@ -88,4 +99,180 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
88
99
|
// An empty array used to fall through as a literal `0` in the heading.
|
|
89
100
|
expect(withEmptyArray.textContent).not.toContain('0');
|
|
90
101
|
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* ActionCard derives the notice's spoken message rather than letting core default it to
|
|
106
|
+
* the children. Core runs `renderToString` over that default during render, which corrupts
|
|
107
|
+
* the hook dispatcher when the children are components — so the derivation is what keeps a
|
|
108
|
+
* notification carrying a `<Button>` from crashing the card on a later re-render.
|
|
109
|
+
*/
|
|
110
|
+
describe('ActionCard notifications', function () {
|
|
111
|
+
beforeEach(function () {
|
|
112
|
+
_a11y.speak.mockClear();
|
|
113
|
+
});
|
|
114
|
+
var spoken = function spoken() {
|
|
115
|
+
return _a11y.speak.mock.calls.map(function (call) {
|
|
116
|
+
return call[0];
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
it('renders a notice for a recognised level', function () {
|
|
120
|
+
var _render5 = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
|
|
121
|
+
title: "Ad Manager",
|
|
122
|
+
notification: "Plugin cannot be installed",
|
|
123
|
+
notificationLevel: "error"
|
|
124
|
+
})),
|
|
125
|
+
container = _render5.container;
|
|
126
|
+
expect(container.querySelector('.components-notice.is-error')).toBeInTheDocument();
|
|
127
|
+
});
|
|
128
|
+
it('renders no notice for an unrecognised or absent level', function () {
|
|
129
|
+
var _render6 = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
|
|
130
|
+
title: "Ad Manager",
|
|
131
|
+
notification: "Something happened",
|
|
132
|
+
notificationLevel: "critical"
|
|
133
|
+
})),
|
|
134
|
+
unknown = _render6.container;
|
|
135
|
+
expect(unknown.querySelector('.components-notice')).not.toBeInTheDocument();
|
|
136
|
+
var _render7 = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
|
|
137
|
+
title: "Ad Manager",
|
|
138
|
+
notification: "Something happened"
|
|
139
|
+
})),
|
|
140
|
+
absent = _render7.container;
|
|
141
|
+
expect(absent.querySelector('.components-notice')).not.toBeInTheDocument();
|
|
142
|
+
});
|
|
143
|
+
it('announces the text inside an element-only notification', function () {
|
|
144
|
+
(0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
|
|
145
|
+
title: "Ad Manager",
|
|
146
|
+
notificationLevel: "error",
|
|
147
|
+
notification: [/*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
|
|
148
|
+
children: "Click here to connect your account."
|
|
149
|
+
}, "connect")]
|
|
150
|
+
}));
|
|
151
|
+
expect(spoken()).toContain('Click here to connect your account.');
|
|
152
|
+
});
|
|
153
|
+
it('announces strings and element text together', function () {
|
|
154
|
+
(0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
|
|
155
|
+
title: "Ad Manager",
|
|
156
|
+
notificationLevel: "success",
|
|
157
|
+
notification: ['Created custom targeting keys: ',
|
|
158
|
+
/*#__PURE__*/
|
|
159
|
+
// eslint-disable-next-line react/jsx-indent
|
|
160
|
+
(0, _jsxRuntime.jsx)("a", {
|
|
161
|
+
href: "https://example.org",
|
|
162
|
+
children: "Visit your GAM dashboard"
|
|
163
|
+
}, "dashboard")]
|
|
164
|
+
}));
|
|
165
|
+
expect(spoken()).toContain('Created custom targeting keys: Visit your GAM dashboard');
|
|
166
|
+
});
|
|
167
|
+
it('announces an Error and renders its message rather than throwing', function () {
|
|
168
|
+
(0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
|
|
169
|
+
title: "Ad Manager",
|
|
170
|
+
notificationLevel: "error",
|
|
171
|
+
notification: new Error('Network unreachable')
|
|
172
|
+
}));
|
|
173
|
+
expect(_react.screen.getByText('Network unreachable')).toBeInTheDocument();
|
|
174
|
+
expect(spoken()).toContain('Network unreachable');
|
|
175
|
+
});
|
|
176
|
+
it('renders HTML notifications in their own wrapper and hands speak() the tags to strip', function () {
|
|
177
|
+
var _render8 = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
|
|
178
|
+
title: "Ad Manager",
|
|
179
|
+
notificationLevel: "error",
|
|
180
|
+
notification: '<p>Install failed.</p><p>Try again.</p>',
|
|
181
|
+
notificationHTML: true
|
|
182
|
+
})),
|
|
183
|
+
container = _render8.container;
|
|
184
|
+
expect(container.querySelectorAll('.newspack-action-card__notification-html > p')).toHaveLength(2);
|
|
185
|
+
expect(spoken()[0]).toContain('<p>Install failed.</p>');
|
|
186
|
+
});
|
|
187
|
+
it('decodes entities in both the announcement and the visible text', function () {
|
|
188
|
+
(0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
|
|
189
|
+
title: "Ad Manager",
|
|
190
|
+
notificationLevel: "error",
|
|
191
|
+
notification: 'Plugin “Foo” could not be activated'
|
|
192
|
+
}));
|
|
193
|
+
expect(_react.screen.getByText('Plugin “Foo” could not be activated')).toBeInTheDocument();
|
|
194
|
+
expect(spoken()[0]).toContain('“Foo”');
|
|
195
|
+
});
|
|
196
|
+
it('decodes entities inside an array of notification parts', function () {
|
|
197
|
+
(0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
|
|
198
|
+
title: "Ad Manager",
|
|
199
|
+
notificationLevel: "error",
|
|
200
|
+
notification: ['Could not reach the publisher’s network.', ' ']
|
|
201
|
+
}));
|
|
202
|
+
expect(_react.screen.getByText(/Could not reach the publisher’s network\./)).toBeInTheDocument();
|
|
203
|
+
expect(spoken()[0]).toContain('publisher’s');
|
|
204
|
+
});
|
|
205
|
+
it('leaves entity decoding to the browser on the HTML branch', function () {
|
|
206
|
+
var _render9 = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
|
|
207
|
+
title: "Ad Manager",
|
|
208
|
+
notificationLevel: "error",
|
|
209
|
+
notification: 'Get it from <a href="https://example.org">the plugin’s site</a>',
|
|
210
|
+
notificationHTML: true
|
|
211
|
+
})),
|
|
212
|
+
container = _render9.container;
|
|
213
|
+
expect(container.querySelector('.newspack-action-card__notification-html a').textContent).toBe('the plugin’s site');
|
|
214
|
+
expect(spoken()[0]).toContain('plugin’s site');
|
|
215
|
+
});
|
|
216
|
+
it('renders a non-string notification as children even when notificationHTML is set', function () {
|
|
217
|
+
var _render0 = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
|
|
218
|
+
title: "Ad Manager",
|
|
219
|
+
notificationLevel: "error",
|
|
220
|
+
notification: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
221
|
+
children: "Network unreachable"
|
|
222
|
+
}),
|
|
223
|
+
notificationHTML: true
|
|
224
|
+
})),
|
|
225
|
+
container = _render0.container;
|
|
226
|
+
expect(_react.screen.getByText('Network unreachable')).toBeInTheDocument();
|
|
227
|
+
expect(container.querySelector('.newspack-action-card__notification-html')).not.toBeInTheDocument();
|
|
228
|
+
});
|
|
229
|
+
it('keeps adjacent element texts apart in the announcement', function () {
|
|
230
|
+
(0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
|
|
231
|
+
title: "Ad Manager",
|
|
232
|
+
notificationLevel: "error",
|
|
233
|
+
notification: [
|
|
234
|
+
/*#__PURE__*/
|
|
235
|
+
// eslint-disable-next-line react/jsx-indent
|
|
236
|
+
(0, _jsxRuntime.jsx)("a", {
|
|
237
|
+
href: "https://example.org",
|
|
238
|
+
children: "Visit your GAM dashboard"
|
|
239
|
+
}, "dashboard"), /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
|
|
240
|
+
type: "button",
|
|
241
|
+
children: "Click here to connect your account."
|
|
242
|
+
}, "connect")]
|
|
243
|
+
}));
|
|
244
|
+
expect(spoken()[0]).toBe('Visit your GAM dashboard Click here to connect your account.');
|
|
245
|
+
});
|
|
246
|
+
it('survives a re-render that changes how many hooked components the notification holds', function () {
|
|
247
|
+
var HookedAction = function HookedAction(_ref) {
|
|
248
|
+
var children = _ref.children;
|
|
249
|
+
var _useState = (0, _element.useState)(children),
|
|
250
|
+
_useState2 = (0, _slicedToArray2["default"])(_useState, 1),
|
|
251
|
+
label = _useState2[0];
|
|
252
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
|
|
253
|
+
type: "button",
|
|
254
|
+
children: label
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
var card = function card(notification) {
|
|
258
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
|
|
259
|
+
title: "Ad Manager",
|
|
260
|
+
notificationLevel: "error",
|
|
261
|
+
notification: notification
|
|
262
|
+
});
|
|
263
|
+
};
|
|
264
|
+
var _render1 = (0, _react.render)(card([/*#__PURE__*/(0, _jsxRuntime.jsx)(HookedAction, {
|
|
265
|
+
children: "Retry"
|
|
266
|
+
}, "retry"), /*#__PURE__*/(0, _jsxRuntime.jsx)(HookedAction, {
|
|
267
|
+
children: "Documentation"
|
|
268
|
+
}, "docs")])),
|
|
269
|
+
rerender = _render1.rerender,
|
|
270
|
+
container = _render1.container;
|
|
271
|
+
expect(function () {
|
|
272
|
+
return rerender(card([/*#__PURE__*/(0, _jsxRuntime.jsx)(HookedAction, {
|
|
273
|
+
children: "Retry"
|
|
274
|
+
}, "retry")]));
|
|
275
|
+
}).not.toThrow();
|
|
276
|
+
expect(container.querySelector('.components-notice.is-error')).toBeInTheDocument();
|
|
277
|
+
});
|
|
91
278
|
});
|
|
@@ -339,18 +339,12 @@
|
|
|
339
339
|
|
|
340
340
|
/* Notifications */
|
|
341
341
|
.newspack-action-card__notification {
|
|
342
|
-
.
|
|
342
|
+
.components-notice {
|
|
343
343
|
margin: 0;
|
|
344
|
-
|
|
345
|
-
&__heading {
|
|
346
|
-
margin-top: 0;
|
|
347
|
-
}
|
|
348
344
|
}
|
|
349
345
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
margin-top: 32px;
|
|
353
|
-
}
|
|
346
|
+
.newspack-action-card__notification-html > * {
|
|
347
|
+
margin: 0;
|
|
354
348
|
}
|
|
355
349
|
}
|
|
356
350
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports["default"] = void 0;
|
|
7
|
+
var _i18n = require("@wordpress/i18n");
|
|
8
|
+
var _icons = require("@wordpress/icons");
|
|
9
|
+
require("./style.scss");
|
|
10
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
11
|
+
/**
|
|
12
|
+
* Debug Badge
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* WordPress dependencies.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Internal dependencies.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Debug Badge component.
|
|
25
|
+
*
|
|
26
|
+
* Gates itself on `window.newspack_aux_data.is_debug_mode`, so consumers render
|
|
27
|
+
* it unconditionally and it stays absent outside debug mode.
|
|
28
|
+
*
|
|
29
|
+
* @return {JSX.Element|null} Debug Badge component, or nothing outside debug mode.
|
|
30
|
+
*/var DebugBadge = function DebugBadge() {
|
|
31
|
+
var _window$newspack_aux_;
|
|
32
|
+
if (!((_window$newspack_aux_ = window.newspack_aux_data) !== null && _window$newspack_aux_ !== void 0 && _window$newspack_aux_.is_debug_mode)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
36
|
+
className: "newspack-debug-badge",
|
|
37
|
+
role: "img",
|
|
38
|
+
"aria-label": (0, _i18n.__)('Debug mode', 'newspack-plugin'),
|
|
39
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.Icon, {
|
|
40
|
+
icon: _icons.bug
|
|
41
|
+
})
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var _default = exports["default"] = DebugBadge;
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
describe('DebugBadge', function () {
|
|
16
|
+
afterEach(function () {
|
|
17
|
+
delete window.newspack_aux_data;
|
|
18
|
+
});
|
|
19
|
+
it('renders the badge and its glyph in debug mode', function () {
|
|
20
|
+
window.newspack_aux_data = {
|
|
21
|
+
is_debug_mode: true
|
|
22
|
+
};
|
|
23
|
+
var _render = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {})),
|
|
24
|
+
container = _render.container;
|
|
25
|
+
expect(container.querySelector('.newspack-debug-badge')).toBeInTheDocument();
|
|
26
|
+
expect(container.querySelector('.newspack-debug-badge > svg')).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
it('names itself for assistive tech', function () {
|
|
29
|
+
window.newspack_aux_data = {
|
|
30
|
+
is_debug_mode: true
|
|
31
|
+
};
|
|
32
|
+
(0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {}));
|
|
33
|
+
expect(_react.screen.getByRole('img', {
|
|
34
|
+
name: 'Debug mode'
|
|
35
|
+
})).toBeInTheDocument();
|
|
36
|
+
});
|
|
37
|
+
it('renders nothing outside debug mode', function () {
|
|
38
|
+
window.newspack_aux_data = {
|
|
39
|
+
is_debug_mode: false
|
|
40
|
+
};
|
|
41
|
+
var _render2 = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {})),
|
|
42
|
+
container = _render2.container;
|
|
43
|
+
expect(container).toBeEmptyDOMElement();
|
|
44
|
+
});
|
|
45
|
+
it('renders nothing when the global is absent', function () {
|
|
46
|
+
var _render3 = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {})),
|
|
47
|
+
container = _render3.container;
|
|
48
|
+
expect(container).toBeEmptyDOMElement();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug Badge
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
@use "~@wordpress/base-styles/colors" as wp-colors;
|
|
6
|
+
@use "~@wordpress/base-styles/variables" as wp-vars;
|
|
7
|
+
@use "../../../colors/colors.module" as np-colors;
|
|
8
|
+
|
|
9
|
+
.newspack-debug-badge {
|
|
10
|
+
background: np-colors.$primary-600;
|
|
11
|
+
border-radius: wp-vars.$radius-round;
|
|
12
|
+
bottom: 0;
|
|
13
|
+
box-shadow: var(--wpds-elevation-sm, #{wp-vars.$elevation-small});
|
|
14
|
+
display: grid;
|
|
15
|
+
height: wp-vars.$button-size;
|
|
16
|
+
margin: 0 wp-vars.$grid-unit-30 wp-vars.$grid-unit-30;
|
|
17
|
+
place-items: center;
|
|
18
|
+
pointer-events: none;
|
|
19
|
+
position: fixed;
|
|
20
|
+
width: wp-vars.$button-size;
|
|
21
|
+
z-index: 9997;
|
|
22
|
+
|
|
23
|
+
> svg {
|
|
24
|
+
fill: wp-colors.$white;
|
|
25
|
+
}
|
|
26
|
+
}
|