newspack-components 4.7.0 → 4.8.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 (251) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/DEVELOPMENT.md +42 -21
  3. package/dist/cjs/_mixins.scss +13 -0
  4. package/dist/cjs/_variables.scss +12 -0
  5. package/dist/cjs/action-card/index.js +18 -10
  6. package/dist/cjs/action-card/index.test.js +91 -0
  7. package/dist/cjs/action-card/style.scss +2 -40
  8. package/dist/cjs/button/index.js +8 -3
  9. package/dist/cjs/button/index.test.js +61 -5
  10. package/dist/cjs/card/core-card.js +0 -2
  11. package/dist/cjs/card/style-core.scss +7 -5
  12. package/dist/cjs/card-feature/index.js +75 -61
  13. package/dist/cjs/card-feature/index.test.js +396 -54
  14. package/dist/cjs/card-feature/style.scss +20 -31
  15. package/dist/cjs/card-form/index.js +5 -5
  16. package/dist/cjs/card-sortable-list/index.js +5 -3
  17. package/dist/cjs/card-sortable-list/index.test.js +50 -0
  18. package/dist/cjs/collapsible-group/context.js +19 -0
  19. package/dist/cjs/collapsible-group/index.js +24 -0
  20. package/dist/cjs/collapsible-group/index.test.js +215 -0
  21. package/dist/cjs/collapsible-group/item.js +61 -0
  22. package/dist/cjs/collapsible-group/root.js +62 -0
  23. package/dist/cjs/collapsible-group/style.scss +47 -0
  24. package/dist/cjs/collapsible-group/types.js +5 -0
  25. package/dist/cjs/empty-state/actions.js +45 -0
  26. package/dist/cjs/empty-state/context.js +41 -0
  27. package/dist/cjs/empty-state/header.js +66 -0
  28. package/dist/cjs/empty-state/index.js +31 -0
  29. package/dist/cjs/empty-state/index.test.js +303 -0
  30. package/dist/cjs/empty-state/root.js +56 -0
  31. package/dist/cjs/empty-state/style.scss +51 -0
  32. package/dist/cjs/empty-state/types.js +5 -0
  33. package/dist/cjs/index.js +34 -20
  34. package/dist/cjs/info-button/index.js +59 -43
  35. package/dist/cjs/info-button/index.test.js +199 -0
  36. package/dist/cjs/info-button/style.scss +41 -9
  37. package/dist/cjs/modal/style.scss +12 -12
  38. package/dist/cjs/newspack-icon/style.scss +4 -4
  39. package/dist/cjs/notice/style.scss +3 -3
  40. package/dist/cjs/page/style.scss +16 -16
  41. package/dist/cjs/plugin-installer/index.js +5 -9
  42. package/dist/cjs/plugin-installer/style.scss +3 -14
  43. package/dist/cjs/section-header/index.js +31 -18
  44. package/dist/cjs/section-header/index.test.js +40 -0
  45. package/dist/cjs/section-header/style.scss +22 -6
  46. package/dist/cjs/settings/DateRangeSetting.js +309 -0
  47. package/dist/cjs/settings/SettingsSection.js +19 -5
  48. package/dist/cjs/settings/SettingsSection.test.js +49 -0
  49. package/dist/cjs/settings/index.js +3 -1
  50. package/dist/cjs/settings/style.scss +40 -7
  51. package/dist/cjs/stat-card/body.js +41 -0
  52. package/dist/cjs/stat-card/constants.js +11 -0
  53. package/dist/cjs/stat-card/context.js +54 -0
  54. package/dist/cjs/stat-card/delta.js +91 -0
  55. package/dist/cjs/stat-card/footer.js +72 -0
  56. package/dist/cjs/stat-card/index.js +47 -0
  57. package/dist/cjs/stat-card/index.test.js +938 -0
  58. package/dist/cjs/stat-card/label.js +66 -0
  59. package/dist/cjs/stat-card/root.js +69 -0
  60. package/dist/cjs/stat-card/secondary.js +38 -0
  61. package/dist/cjs/stat-card/style.scss +89 -0
  62. package/dist/cjs/stat-card/types.js +5 -0
  63. package/dist/cjs/stat-card/value.js +78 -0
  64. package/dist/cjs/status-indicator/index.js +66 -0
  65. package/dist/cjs/status-indicator/index.test.js +101 -0
  66. package/dist/cjs/status-indicator/statuses.js +39 -0
  67. package/dist/cjs/status-indicator/style.scss +9 -0
  68. package/dist/cjs/status-indicator/types.js +5 -0
  69. package/dist/cjs/style.scss +9 -9
  70. package/dist/cjs/tabbed-navigation/style.scss +6 -4
  71. package/dist/cjs/types.js +5 -0
  72. package/dist/cjs/web-preview/style.scss +11 -11
  73. package/dist/cjs/with-wizard/style.scss +10 -10
  74. package/dist/cjs/with-wizard-screen/style.scss +9 -9
  75. package/dist/cjs/wizard/index.js +33 -4
  76. package/dist/cjs/wizard/store/index.js +23 -1
  77. package/dist/esm/_mixins.scss +13 -0
  78. package/dist/esm/_variables.scss +12 -0
  79. package/dist/esm/action-card/index.js +18 -10
  80. package/dist/esm/action-card/index.test.js +89 -0
  81. package/dist/esm/action-card/style.scss +2 -40
  82. package/dist/esm/button/index.js +9 -4
  83. package/dist/esm/button/index.test.js +63 -5
  84. package/dist/esm/card/core-card.js +0 -2
  85. package/dist/esm/card/style-core.scss +7 -5
  86. package/dist/esm/card-feature/index.js +78 -62
  87. package/dist/esm/card-feature/index.test.js +397 -55
  88. package/dist/esm/card-feature/style.scss +20 -31
  89. package/dist/esm/card-form/index.js +5 -5
  90. package/dist/esm/card-sortable-list/index.js +6 -4
  91. package/dist/esm/card-sortable-list/index.test.js +46 -0
  92. package/dist/esm/collapsible-group/context.js +13 -0
  93. package/dist/esm/collapsible-group/index.js +17 -0
  94. package/dist/esm/collapsible-group/index.test.js +215 -0
  95. package/dist/esm/collapsible-group/item.js +53 -0
  96. package/dist/esm/collapsible-group/root.js +54 -0
  97. package/dist/esm/collapsible-group/style.scss +47 -0
  98. package/dist/esm/collapsible-group/types.js +1 -0
  99. package/dist/esm/empty-state/actions.js +37 -0
  100. package/dist/esm/empty-state/context.js +35 -0
  101. package/dist/esm/empty-state/header.js +58 -0
  102. package/dist/esm/empty-state/index.js +23 -0
  103. package/dist/esm/empty-state/index.test.js +299 -0
  104. package/dist/esm/empty-state/root.js +56 -0
  105. package/dist/esm/empty-state/style.scss +51 -0
  106. package/dist/esm/empty-state/types.js +1 -0
  107. package/dist/esm/index.js +4 -2
  108. package/dist/esm/info-button/index.js +54 -43
  109. package/dist/esm/info-button/index.test.js +195 -0
  110. package/dist/esm/info-button/style.scss +41 -9
  111. package/dist/esm/modal/style.scss +12 -12
  112. package/dist/esm/newspack-icon/style.scss +4 -4
  113. package/dist/esm/notice/style.scss +3 -3
  114. package/dist/esm/page/style.scss +16 -16
  115. package/dist/esm/plugin-installer/index.js +5 -9
  116. package/dist/esm/plugin-installer/style.scss +3 -14
  117. package/dist/esm/section-header/index.js +27 -14
  118. package/dist/esm/section-header/index.test.js +36 -0
  119. package/dist/esm/section-header/style.scss +22 -6
  120. package/dist/esm/settings/DateRangeSetting.js +304 -0
  121. package/dist/esm/settings/SettingsSection.js +16 -3
  122. package/dist/esm/settings/SettingsSection.test.js +45 -0
  123. package/dist/esm/settings/index.js +3 -1
  124. package/dist/esm/settings/style.scss +40 -7
  125. package/dist/esm/stat-card/body.js +36 -0
  126. package/dist/esm/stat-card/constants.js +5 -0
  127. package/dist/esm/stat-card/context.js +48 -0
  128. package/dist/esm/stat-card/delta.js +86 -0
  129. package/dist/esm/stat-card/footer.js +67 -0
  130. package/dist/esm/stat-card/index.js +33 -0
  131. package/dist/esm/stat-card/index.test.js +939 -0
  132. package/dist/esm/stat-card/label.js +61 -0
  133. package/dist/esm/stat-card/root.js +64 -0
  134. package/dist/esm/stat-card/secondary.js +33 -0
  135. package/dist/esm/stat-card/style.scss +89 -0
  136. package/dist/esm/stat-card/types.js +1 -0
  137. package/dist/esm/stat-card/value.js +73 -0
  138. package/dist/esm/status-indicator/index.js +52 -0
  139. package/dist/esm/status-indicator/index.test.js +96 -0
  140. package/dist/esm/status-indicator/statuses.js +33 -0
  141. package/dist/esm/status-indicator/style.scss +9 -0
  142. package/dist/esm/status-indicator/types.js +1 -0
  143. package/dist/esm/style.scss +9 -9
  144. package/dist/esm/tabbed-navigation/style.scss +6 -4
  145. package/dist/esm/types.js +1 -0
  146. package/dist/esm/web-preview/style.scss +11 -11
  147. package/dist/esm/with-wizard/style.scss +10 -10
  148. package/dist/esm/with-wizard-screen/style.scss +9 -9
  149. package/dist/esm/wizard/index.js +38 -7
  150. package/dist/esm/wizard/store/index.js +23 -1
  151. package/package.json +1 -1
  152. package/src/_mixins.scss +13 -0
  153. package/src/_variables.scss +12 -0
  154. package/src/action-card/action-card.d.ts +3 -2
  155. package/src/action-card/index.js +12 -12
  156. package/src/action-card/index.test.js +56 -0
  157. package/src/action-card/style.scss +2 -40
  158. package/src/button/index.test.js +52 -5
  159. package/src/button/index.tsx +9 -4
  160. package/src/card/core-card.js +0 -2
  161. package/src/card/style-core.scss +7 -5
  162. package/src/card-feature/README.md +68 -27
  163. package/src/card-feature/index.test.js +242 -27
  164. package/src/card-feature/index.tsx +77 -67
  165. package/src/card-feature/style.scss +20 -31
  166. package/src/card-form/README.md +10 -10
  167. package/src/card-form/index.tsx +4 -8
  168. package/src/card-sortable-list/README.md +4 -5
  169. package/src/card-sortable-list/index.test.js +29 -0
  170. package/src/card-sortable-list/index.tsx +7 -4
  171. package/src/collapsible-group/README.md +50 -0
  172. package/src/collapsible-group/context.ts +13 -0
  173. package/src/collapsible-group/index.test.js +168 -0
  174. package/src/collapsible-group/index.tsx +17 -0
  175. package/src/collapsible-group/item.tsx +42 -0
  176. package/src/collapsible-group/root.tsx +42 -0
  177. package/src/collapsible-group/style.scss +47 -0
  178. package/src/collapsible-group/types.ts +22 -0
  179. package/src/drawer/README.md +2 -2
  180. package/src/empty-state/README.md +165 -0
  181. package/src/empty-state/actions.tsx +37 -0
  182. package/src/empty-state/context.ts +40 -0
  183. package/src/empty-state/header.tsx +48 -0
  184. package/src/empty-state/index.test.js +247 -0
  185. package/src/empty-state/index.tsx +22 -0
  186. package/src/empty-state/root.tsx +39 -0
  187. package/src/empty-state/style.scss +51 -0
  188. package/src/empty-state/types.ts +33 -0
  189. package/src/index.js +4 -2
  190. package/src/info-button/README.md +115 -0
  191. package/src/info-button/index.test.js +163 -0
  192. package/src/info-button/index.tsx +64 -0
  193. package/src/info-button/style.scss +41 -9
  194. package/src/modal/style.scss +12 -12
  195. package/src/newspack-icon/style.scss +4 -4
  196. package/src/notice/style.scss +3 -3
  197. package/src/page/style.scss +16 -16
  198. package/src/plugin-installer/index.js +3 -9
  199. package/src/plugin-installer/style.scss +3 -14
  200. package/src/section-header/index.js +27 -14
  201. package/src/section-header/index.test.js +28 -0
  202. package/src/section-header/style.scss +22 -6
  203. package/src/settings/DateRangeSetting.js +266 -0
  204. package/src/settings/SettingsSection.js +24 -4
  205. package/src/settings/SettingsSection.test.js +45 -0
  206. package/src/settings/index.js +2 -0
  207. package/src/settings/style.scss +40 -7
  208. package/src/stat-card/README.md +434 -0
  209. package/src/stat-card/body.tsx +28 -0
  210. package/src/stat-card/constants.ts +5 -0
  211. package/src/stat-card/context.ts +53 -0
  212. package/src/stat-card/delta.tsx +88 -0
  213. package/src/stat-card/footer.tsx +68 -0
  214. package/src/stat-card/index.test.js +818 -0
  215. package/src/stat-card/index.tsx +48 -0
  216. package/src/stat-card/label.tsx +61 -0
  217. package/src/stat-card/root.tsx +40 -0
  218. package/src/stat-card/secondary.tsx +27 -0
  219. package/src/stat-card/style.scss +89 -0
  220. package/src/stat-card/types.ts +84 -0
  221. package/src/stat-card/value.tsx +72 -0
  222. package/src/status-indicator/README.md +123 -0
  223. package/src/status-indicator/index.test.js +70 -0
  224. package/src/status-indicator/index.tsx +39 -0
  225. package/src/status-indicator/statuses.ts +35 -0
  226. package/src/status-indicator/style.scss +9 -0
  227. package/src/status-indicator/types.ts +28 -0
  228. package/src/style.scss +9 -9
  229. package/src/tabbed-navigation/style.scss +6 -4
  230. package/src/types.ts +25 -0
  231. package/src/web-preview/style.scss +11 -11
  232. package/src/with-wizard/style.scss +10 -10
  233. package/src/with-wizard-screen/style.scss +9 -9
  234. package/src/wizard/index.js +49 -7
  235. package/src/wizard/store/index.js +26 -1
  236. package/dist/cjs/accordion/index.js +0 -72
  237. package/dist/cjs/accordion/index.test.js +0 -80
  238. package/dist/cjs/accordion/style.scss +0 -35
  239. package/dist/cjs/badge/index.js +0 -32
  240. package/dist/cjs/badge/style.scss +0 -36
  241. package/dist/esm/accordion/index.js +0 -63
  242. package/dist/esm/accordion/index.test.js +0 -79
  243. package/dist/esm/accordion/style.scss +0 -35
  244. package/dist/esm/badge/index.js +0 -24
  245. package/dist/esm/badge/style.scss +0 -36
  246. package/src/accordion/index.js +0 -46
  247. package/src/accordion/index.test.js +0 -62
  248. package/src/accordion/style.scss +0 -35
  249. package/src/badge/index.tsx +0 -26
  250. package/src/badge/style.scss +0 -36
  251. package/src/info-button/index.js +0 -38
package/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ # 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
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **editor:** stop console warnings from Button, cards and Collections ([#907](https://github.com/Automattic/newspack-workspace/issues/907)) ([70e41cd](https://github.com/Automattic/newspack-workspace/commit/70e41cd116a295a391af966f924c269c567967a2))
7
+
8
+
9
+ ### Features
10
+
11
+ * **audience:** date range segmentation for ESP date fields ([#767](https://github.com/Automattic/newspack-workspace/issues/767)) ([21c4732](https://github.com/Automattic/newspack-workspace/commit/21c4732a2b8cbadea1e7d6710ca22d22b3282cef))
12
+ * **components:** Accordion becomes CollapsibleGroup (DSGNEWS-215) ([e6639f2](https://github.com/Automattic/newspack-workspace/commit/e6639f2214d591bba3bc38fb7f042719ca58d3ea))
13
+ * **components:** adopt design system badges (DSGNEWS-215) ([fc4c8f7](https://github.com/Automattic/newspack-workspace/commit/fc4c8f73ccba6ec4075e09578a43938a40eda60f))
14
+ * **components:** rebuild the info button on Popover (DSGNEWS-215) ([#887](https://github.com/Automattic/newspack-workspace/issues/887)) ([31f996d](https://github.com/Automattic/newspack-workspace/commit/31f996d25051a5f861e94cc03b42bb7d9afc10da))
15
+ * **components:** shared EmptyState component (DSGNEWS-211) ([#854](https://github.com/Automattic/newspack-workspace/issues/854)) ([6c31f8c](https://github.com/Automattic/newspack-workspace/commit/6c31f8cac683570e36f3768b0be6061b55d7c946))
16
+ * **components:** shared StatCard component (DSGNEWS-212) ([#864](https://github.com/Automattic/newspack-workspace/issues/864)) ([ac434c2](https://github.com/Automattic/newspack-workspace/commit/ac434c2bc6777f99b65e18fa70e10100ed077ca2))
17
+ * **components:** use an icon and label for DataViews Status columns (DSGNEWS-222) ([aa1d404](https://github.com/Automattic/newspack-workspace/commit/aa1d40422ccddad0d34de67ed444f73f6d14ea49))
18
+ * **content-gate:** share one metering allowance across gates (NPPD-2191, [#919](https://github.com/Automattic/newspack-workspace/issues/919)) ([cb79f9a](https://github.com/Automattic/newspack-workspace/commit/cb79f9a79990975d48acde04b57e2746fd694bdd))
19
+ * **subscriber-discounts:** subscriber discounts on WooCommerce products (NPPD-1794, [#760](https://github.com/Automattic/newspack-workspace/issues/760)) ([3ef9b8a](https://github.com/Automattic/newspack-workspace/commit/3ef9b8ac6f318437777027445ef835d30ec6c8c1)), closes [#8217](https://github.com/Automattic/newspack-workspace/issues/8217)
20
+
1
21
  # newspack-components [4.7.0](https://github.com/Automattic/newspack-workspace/compare/newspack-components@4.6.3...newspack-components@4.7.0) (2026-08-31)
2
22
 
3
23
 
package/DEVELOPMENT.md CHANGED
@@ -74,39 +74,44 @@ When building a screen, use the **spacing scale** (8px unit: 16, 24, 32, 48, 64)
74
74
 
75
75
  ### Layout Components
76
76
 
77
+ - **`BoxContrast`** – High-contrast content box for emphasis.
77
78
  - **`Card`** – Container for a logical block of content; use for grouping related settings. Default vertical margin is 32px so cards stack with consistent rhythm. Use `noBorder` when cards sit inside another card (e.g. ActionCard children).
78
- - **`Grid`** – Use for laying out several items in columns (e.g. multiple controls or cards). Default gap is 32px; use `columns` and `gutter` modifiers (8, 16, 24, 32) when you need tighter or looser spacing. For a single row or a simple vertical stack, prefer **VStack** (or HStack) in new code; Grid is still used that way in many places and is fine to leave as-is until we refactor.
79
79
  - **`Divider`** – Use between logical sections (e.g. between ActionCards) to separate content without another card. Margins are 32px (64px at larger breakpoints) so spacing stays consistent with the rest of the layout.
80
+ - **`EmptyState`** – Use when a list has nothing in it yet, or an onboarding view stands in for a feature that is not set up. Compound: `EmptyState.Root` wraps `EmptyState.Header` and `EmptyState.Actions`. Render it only when the *unfiltered* collection is empty, so a search matching nothing keeps the DataViews "no results" treatment. See [src/empty-state/README.md](src/empty-state/README.md).
81
+ - **`Grid`** – Use for laying out several items in columns (e.g. multiple controls or cards). Default gap is 32px; use `columns` and `gutter` modifiers (8, 16, 24, 32) when you need tighter or looser spacing. For a single row or a simple vertical stack, prefer **VStack** (or HStack) in new code; Grid is still used that way in many places and is fine to leave as-is until we refactor.
80
82
  - **`SectionHeader`** – Use to start a new section; pair with a short description so the section’s goal is clear. Top margin (64px) and bottom (32px) create clear separation from previous content and the section body.
81
- - **`BoxContrast`** – High-contrast content box for emphasis.
83
+ - **`StatCard`** – Use for a single headline figure with a label above it and a description below. Compound: `StatCard.Root` wraps `Label`, `Body` (holding `Value` and an optional `Secondary`) and `Footer`, with `Delta` riding in the `Value`'s `suffix` for a change beside the figure. Format the figure yourself and pass a string; a `null`, `undefined` or blank `value` gets the shared null glyph rather than a misleading zero. The figure is sized by a container query, so give the cards a definite width (a Grid track, a `flex: 1` item) and equal widths across a row to keep one type scale. See [src/stat-card/README.md](src/stat-card/README.md).
82
84
 
83
85
  ### Form Components
84
86
 
85
- - **`Button`** - Enhanced button component (wraps WordPress Button with routing support)
86
- - **`TextControl`** - Text input with Newspack styling and required field support
87
- - **`RadioControl`** - Radio button group control
88
- - **`ColorPicker`** - Color selection component
89
- - **`ImageUpload`** - Image upload and selection component
90
- - **`FormTokenField`** - Token input field for tags/categories; prefer the Newspack component over the core `FormTokenField` because it also supports a `description` prop for help text like other controls.
91
87
  - **`AutocompleteTokenField`** - Autocomplete token field (can be used in block editor)
92
- - **`AutocompleteWithSuggestions`** - Autocomplete with custom suggestions
93
88
  - **`AutocompleteWithLatestPosts`** - Autocomplete with latest posts
89
+ - **`AutocompleteWithSuggestions`** - Autocomplete with custom suggestions
90
+ - **`Button`** - Enhanced button component (wraps WordPress Button with routing support)
94
91
  - **`CategoryAutocomplete`** - Category-specific autocomplete
92
+ - **`ColorPicker`** - Color selection component
95
93
  - **`CustomSelectControl`** - Custom select dropdown component
94
+ - **`FormTokenField`** - Token input field for tags/categories; prefer the Newspack component over the core `FormTokenField` because it also supports a `description` prop for help text like other controls.
95
+ - **`ImageUpload`** - Image upload and selection component
96
+ - **`RadioControl`** - Radio button group control
97
+ - **`TextControl`** - Text input with Newspack styling and required field support
96
98
 
97
99
  ### Content Components
98
100
 
99
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
+ - **`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.
100
103
  - **`Notice`** – Use for outcome feedback (success/error/warning) or short contextual messages. Vertical margin is 32px so notices don’t collide with cards; keep one primary message per area when possible.
101
- - **`Waiting`** – Loading state indicator.
102
104
  - **`ProgressBar`** – Progress indicator.
103
- - **`Accordion`** / **`AccordionPanel`** – Container for one or more collapsible panels.
104
105
  - **`StepsList`** / **`StepsListItem`** – Step-by-step list components.
105
106
  - **`StyleCard`** – Style preview card.
107
+ - **`Waiting`** – Loading state indicator.
106
108
 
107
109
  ### Wizard Components
108
110
 
109
- - **`Wizard`** - Main wizard container with tabbed navigation and data fetching
111
+ - **`Footer`** - Wizard footer component
112
+ - **`Handoff`** - Handoff message component for external integrations
113
+ - **`HandoffMessage`** - Handoff message display component
114
+ - **`TabbedNavigation`** - Tab navigation component
110
115
  - **`withWizard`** - Higher-order component for wizard screens (legacy pattern, class-based)
111
116
  - Provides plugin management, error handling, loading states
112
117
  - Used in older wizards like Setup Wizard
@@ -116,25 +121,22 @@ When building a screen, use the **spacing scale** (8px unit: 16, 24, 32, 48, 64)
116
121
  - Provides header, tabbed navigation, button actions, handoff messages
117
122
  - Used in newer wizards like Audience Management
118
123
  - Passes props: `renderPrimaryButton`, plus all original component props
119
- - **`TabbedNavigation`** - Tab navigation component
120
- - **`Footer`** - Wizard footer component
121
- - **`Handoff`** - Handoff message component for external integrations
122
- - **`HandoffMessage`** - Handoff message display component
124
+ - **`Wizard`** - Main wizard container with tabbed navigation and data fetching
123
125
 
124
126
  ### Plugin Management Components
125
127
 
126
128
  - **`PluginInstaller`** - Plugin installation and activation component
127
- - **`PluginToggle`** - Plugin enable/disable toggle
128
129
  - **`PluginSettings`** - Plugin settings configuration component
130
+ - **`PluginToggle`** - Plugin enable/disable toggle
129
131
 
130
132
  ### Utility Components
131
133
 
134
+ - **`GlobalNotices`** - Global notice system component
135
+ - **`InfoButton`** - Reveals supplementary context from a `description` prop. Anything a reader needs in order to use a control belongs in visible help text instead
132
136
  - **`Modal`** - Modal dialog component
137
+ - **`NewspackIcon`** - Newspack icon wrapper component
133
138
  - **`Popover`** - Popover component
134
139
  - **`WebPreview`** - Web preview iframe component
135
- - **`NewspackIcon`** - Newspack icon wrapper component
136
- - **`InfoButton`** - Info button with tooltip
137
- - **`GlobalNotices`** - Global notice system component
138
140
 
139
141
  ### Settings Components
140
142
 
@@ -144,9 +146,9 @@ When building a screen, use the **spacing scale** (8px unit: 16, 24, 32, 48, 64)
144
146
  ### Utilities & Hooks
145
147
 
146
148
  - **`hooks`** - Custom React hooks (e.g., `useObjectState`, `usePrompt`, `useOnClickOutside`)
147
- - **`utils`** - Utility functions (e.g., `confirmAction`, color utilities)
148
149
  - **`Router`** - Proxied React Router import (use instead of direct `react-router-dom` import)
149
150
  - Note: This package currently uses [React Router v5](https://v5.reactrouter.com/). Please refer to v5 documentation for API details.
151
+ - **`utils`** - Utility functions (e.g., `confirmAction`, color utilities)
150
152
 
151
153
  ## Import Patterns
152
154
 
@@ -205,6 +207,24 @@ import { Button, Card, Notice } from '../../../../../packages/components/src';
205
207
  import * as Components from '../../../../../packages/components/src';
206
208
  ```
207
209
 
210
+ **Reach past the barrel from inside the package.** `src/index.js` imports
211
+ `src/style.scss`, which redefines `--wp-admin-theme-color` and its variants on
212
+ `:root`. Any module that pulls in the barrel carries those global overrides into
213
+ whatever bundle it lands in, which is wrong for an editor or front-end bundle.
214
+ Import each component from its own directory instead:
215
+
216
+ ```jsx
217
+ // ✅ CORRECT – within packages/components
218
+ import Grid from '../grid';
219
+ import InfoButton from '../info-button';
220
+
221
+ // ❌ WRONG – drags the package's :root overrides along
222
+ import { Grid, InfoButton } from '../';
223
+ ```
224
+
225
+ Most of the package still reaches the barrel. Treat this as the bar for new and
226
+ edited modules rather than a description of how things stand.
227
+
208
228
  ### Router Import Pattern
209
229
 
210
230
  **Always use the proxied router** - Never import `react-router-dom` directly in source code:
@@ -591,6 +611,7 @@ Newspack components use SCSS with BEM-ish naming conventions and a consistent sp
591
611
  - **Elements**: Use `__` for elements that are part of a larger block-level component (e.g. `.newspack-card__header-content`)
592
612
  - **WordPress colors:** Use WordPress design system colors (see [Colors Development Guide](../colors/DEVELOPMENT.md))
593
613
  - **Custom styles:** Component-specific styles live in `packages/components/src/{component}/style.scss`
614
+ - **Shared values:** Reach for a `@wordpress/base-styles` variable first. Where the scale has no counterpart, the value goes in `packages/components/src/_variables.scss` and is used by name, rather than written as a literal in one component's stylesheet
594
615
 
595
616
  ### Spacing scale (design system)
596
617
 
@@ -0,0 +1,13 @@
1
+ @use "../../colors/colors.module" as colors;
2
+ @use "~@wordpress/base-styles/colors" as wp-colors;
3
+ @use "~@wordpress/base-styles/variables" as wp-vars;
4
+
5
+ @mixin text-muted {
6
+ /* stylelint-disable-next-line plugin-wpds/no-unknown-ds-tokens -- the plugin's token list lags the prebuilt design-tokens.css names. */
7
+ color: var( --wpds-color-foreground-content-neutral-weak, #{wp-colors.$gray-700} );
8
+ }
9
+
10
+ @mixin wpds-focus-ring {
11
+ /* stylelint-disable-next-line plugin-wpds/no-unknown-ds-tokens -- the plugin's token list lags the prebuilt design-tokens.css names. */
12
+ outline: var( --wpds-border-width-focus, var( --wp-admin-border-width-focus, #{wp-vars.$border-width-focus-fallback} ) ) solid var( --wpds-color-stroke-focus, var( --wp-admin-theme-color, #{colors.$primary-600} ) );
13
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * SCSS Variables.
3
+ *
4
+ * The steps this package needs above the @wordpress/base-styles scale. Please
5
+ * reach for a base-styles variable first, and only add here when there is no
6
+ * counterpart and you're pretty sure the value will be reused in many places.
7
+ * It's purely for UI. Don't add component-specific values, which belong in that
8
+ * component's own stylesheet.
9
+ */
10
+
11
+ $font-size-3x-large: 48px;
12
+ $font-line-height-3x-large: 1.125;
@@ -11,6 +11,7 @@ var _components = require("@wordpress/components");
11
11
  var _element = require("@wordpress/element");
12
12
  var _i18n = require("@wordpress/i18n");
13
13
  var _icons = require("@wordpress/icons");
14
+ var _ui = require("@wordpress/ui");
14
15
  var _ = require("../");
15
16
  var _actionCardD = require("./action-card.d.ts");
16
17
  require("./style.scss");
@@ -31,8 +32,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
31
32
  * @return {JSX.Element} ActionCard component.
32
33
  */
33
34
  var ActionCard = function ActionCard(_ref) {
34
- var badge = _ref.badge,
35
- badgeLevel = _ref.badgeLevel,
35
+ var badges = _ref.badges,
36
36
  className = _ref.className,
37
37
  checkbox = _ref.checkbox,
38
38
  children = _ref.children,
@@ -85,6 +85,13 @@ var ActionCard = function ActionCard(_ref) {
85
85
  dragIndex = _ref.dragIndex,
86
86
  dragWrapperRef = _ref.dragWrapperRef,
87
87
  onDragCallback = _ref.onDragCallback;
88
+ // A badge with no label paints an empty pill, since the library Badge styles its
89
+ // wrapper rather than its text. Callers derive labels from data that can be absent
90
+ // (a gateway with no connection status, an unmapped placement), so drop those here
91
+ // rather than asking every caller to guard its own array.
92
+ var visibleBadges = (badges || []).filter(function (badge) {
93
+ return badge === null || badge === void 0 ? void 0 : badge.label;
94
+ });
88
95
  var _useState = (0, _element.useState)(Boolean(isExpanded)),
89
96
  _useState2 = (0, _slicedToArray2["default"])(_useState, 2),
90
97
  expanded = _useState2[0],
@@ -132,7 +139,6 @@ var ActionCard = function ActionCard(_ref) {
132
139
  var togglePositionClass = togglePosition === 'trailing' ? 'is-toggle-trailing' : 'is-toggle-leading';
133
140
  var hasInternalLink = href && href.indexOf('http') !== 0;
134
141
  var isDisplayingSecondaryAction = secondaryActionText && onSecondaryActionClick;
135
- var badges = !Array.isArray(badge) && badge ? [badge] : badge;
136
142
  var HeadingTag = "h".concat(heading);
137
143
  var cardContent = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
138
144
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
@@ -179,10 +185,12 @@ var ActionCard = function ActionCard(_ref) {
179
185
  },
180
186
  children: title
181
187
  }), !titleLink && !expandable && title]
182
- })), (badges === null || badges === void 0 ? void 0 : badges.length) && badges.map(function (badgeText, i) {
183
- return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
184
- className: "newspack-action-card__badge newspack-action-card__badge-level-".concat(badgeLevel),
185
- children: badgeText
188
+ })), visibleBadges.map(function (_ref2, i) {
189
+ var label = _ref2.label,
190
+ intent = _ref2.intent;
191
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ui.Badge, {
192
+ intent: intent !== null && intent !== void 0 ? intent : 'none',
193
+ children: label
186
194
  }, "badge-".concat(i));
187
195
  })]
188
196
  }), description && /*#__PURE__*/(0, _jsxRuntime.jsxs)("p", {
@@ -315,9 +323,9 @@ var ActionCard = function ActionCard(_ref) {
315
323
  onDragStart: handleDragStart,
316
324
  onDragEnd: handleDragEnd,
317
325
  onDragOver: handleDragOver,
318
- children: function children(_ref2) {
319
- var onDraggableStart = _ref2.onDraggableStart,
320
- onDraggableEnd = _ref2.onDraggableEnd;
326
+ children: function children(_ref3) {
327
+ var onDraggableStart = _ref3.onDraggableStart,
328
+ onDraggableEnd = _ref3.onDraggableEnd;
321
329
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_.Card, {
322
330
  className: classes,
323
331
  onClick: simple && onClick,
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _react = require("@testing-library/react");
5
+ var _index = _interopRequireDefault(require("./index"));
6
+ var _jsxRuntime = require("react/jsx-runtime");
7
+ /**
8
+ * External dependencies.
9
+ */
10
+
11
+ /**
12
+ * Internal dependencies.
13
+ */
14
+
15
+ /**
16
+ * The library Badge styles its wrapper rather than its text, so a badge with no label
17
+ * paints a bare coloured pill. Callers build the `badges` array from data that can be
18
+ * absent — a payment gateway reports no connection status until it is enabled, a prompt
19
+ * can sit on a placement with no entry in the placement map — and an array literal is
20
+ * always non-empty, so the component has to drop those itself.
21
+ */describe('ActionCard badges', function () {
22
+ var badgeText = function badgeText() {
23
+ return _react.screen.queryAllByText(function (_, node) {
24
+ return /__badge/.test((node === null || node === void 0 ? void 0 : node.className) || '');
25
+ });
26
+ };
27
+ it('renders a badge that has a label', function () {
28
+ (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
29
+ title: "Stripe",
30
+ badges: [{
31
+ label: 'Connected',
32
+ intent: 'stable'
33
+ }]
34
+ }));
35
+ expect(_react.screen.getByText('Connected')).toBeInTheDocument();
36
+ });
37
+ it('renders no badge when the label is null', function () {
38
+ var _render = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
39
+ title: "Stripe",
40
+ badges: [{
41
+ label: null,
42
+ intent: 'informational'
43
+ }]
44
+ })),
45
+ container = _render.container;
46
+ expect(container.querySelectorAll('[class*="__badge"]')).toHaveLength(0);
47
+ });
48
+ it('renders no badge when the label is undefined or empty', function () {
49
+ var _render2 = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
50
+ title: "Stripe",
51
+ badges: [{
52
+ label: undefined
53
+ }, {
54
+ label: ''
55
+ }]
56
+ })),
57
+ container = _render2.container;
58
+ expect(container.querySelectorAll('[class*="__badge"]')).toHaveLength(0);
59
+ });
60
+ it('keeps the labelled badges and drops only the empty ones', function () {
61
+ (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
62
+ title: "Plans",
63
+ badges: [{
64
+ label: 'Premium'
65
+ }, {
66
+ label: null
67
+ }, {
68
+ label: 'Archived',
69
+ intent: 'draft'
70
+ }]
71
+ }));
72
+ expect(_react.screen.getByText('Premium')).toBeInTheDocument();
73
+ expect(_react.screen.getByText('Archived')).toBeInTheDocument();
74
+ expect(badgeText()).toHaveLength(2);
75
+ });
76
+ it('renders nothing for an absent or empty badges prop', function () {
77
+ var _render3 = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
78
+ title: "Stripe"
79
+ })),
80
+ withoutProp = _render3.container;
81
+ expect(withoutProp.querySelectorAll('[class*="__badge"]')).toHaveLength(0);
82
+ var _render4 = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_index["default"], {
83
+ title: "Stripe",
84
+ badges: []
85
+ })),
86
+ withEmptyArray = _render4.container;
87
+ expect(withEmptyArray.querySelectorAll('[class*="__badge"]')).toHaveLength(0);
88
+ // An empty array used to fall through as a literal `0` in the heading.
89
+ expect(withEmptyArray.textContent).not.toContain('0');
90
+ });
91
+ });
@@ -4,6 +4,7 @@
4
4
 
5
5
  @use "sass:color";
6
6
  @use "~@wordpress/base-styles/colors" as wp-colors;
7
+ @use "~@wordpress/base-styles/variables" as wp-vars;
7
8
 
8
9
  .newspack-card.newspack-action-card {
9
10
  padding: 0;
@@ -196,11 +197,7 @@
196
197
  align-items: center;
197
198
  display: flex;
198
199
  flex-wrap: wrap;
199
- margin-right: -8px;
200
-
201
- .newspack-action-card__title {
202
- margin-right: 8px;
203
- }
200
+ gap: wp-vars.$grid-unit-10;
204
201
 
205
202
  a {
206
203
  color: inherit;
@@ -372,41 +369,6 @@
372
369
  margin: 0;
373
370
  }
374
371
 
375
- /* Badge */
376
- .newspack-action-card__badge {
377
- background: wp-colors.$gray-100;
378
- border-radius: 2px;
379
- color: wp-colors.$gray-900;
380
- display: block;
381
- flex: 0 0 auto;
382
- font-size: 12px;
383
- font-weight: 400;
384
- line-height: 1.333;
385
- margin: 0 8px 0 0;
386
- padding: 2px 4px;
387
-
388
- &.newspack-action-card__badge-level-warning {
389
- background-color: color-mix(in srgb, #fff 90%, wp-colors.$alert-yellow);
390
- color: color-mix(in srgb, #000 50%, wp-colors.$alert-yellow);
391
- }
392
- &.newspack-action-card__badge-level-error {
393
- background-color: color-mix(in srgb, #fff 90%, wp-colors.$alert-red);
394
- color: color-mix(in srgb, #000 50%, wp-colors.$alert-red);
395
- }
396
- &.newspack-action-card__badge-level-success {
397
- background-color: color-mix(in srgb, #fff 90%, wp-colors.$alert-green);
398
- color: color-mix(in srgb, #000 50%, wp-colors.$alert-green);
399
- }
400
- &.newspack-action-card__badge-level-brand {
401
- background-color: color-mix(in srgb, #fff 90%, var(--wp-admin-theme-color));
402
- color: color-mix(in srgb, #000 50%, var(--wp-admin-theme-color));
403
- }
404
-
405
- + .newspack-action-card__badge {
406
- margin-left: -4px;
407
- }
408
- }
409
-
410
372
  /* Description Card */
411
373
  &.newspack-description-card {
412
374
  font-style: italic;
@@ -34,7 +34,7 @@ var useHistory = _router["default"].useHistory;
34
34
  var isJavascriptHref = function isJavascriptHref(href) {
35
35
  return 'string' === typeof href && href.replace(/[\u0000-\u0020]/g, '').toLowerCase().startsWith('javascript:');
36
36
  };
37
- var Button = function Button(_ref) {
37
+ var Button = (0, _element.forwardRef)(function (_ref, ref) {
38
38
  var href = _ref.href,
39
39
  _ref$loading = _ref.loading,
40
40
  loading = _ref$loading === void 0 ? undefined : _ref$loading,
@@ -89,9 +89,14 @@ var Button = function Button(_ref) {
89
89
  if (isAwaitingOnClick) {
90
90
  otherProps.disabled = true;
91
91
  }
92
+ // `loading` is this component's own prop name; @wordpress/components' Button
93
+ // expresses the same state as `isBusy`. Forwarding `loading` verbatim would
94
+ // land it on the DOM node, which React rejects as a non-boolean attribute.
92
95
  // @ts-expect-error - @wordpress/components' Button can only have either href or onClick, not both.
93
96
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, _objectSpread({
94
- loading: loading ? true : undefined
97
+ ref: ref,
98
+ isBusy: loading ? true : undefined
95
99
  }, otherProps));
96
- };
100
+ });
101
+ Button.displayName = 'Button';
97
102
  var _default = exports["default"] = Button;
@@ -4,12 +4,15 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
5
5
  var _react = require("@testing-library/react");
6
6
  var _reactRouterDom = require("react-router-dom");
7
+ var _element = require("@wordpress/element");
7
8
  var _ = _interopRequireDefault(require("./"));
8
9
  var _jsxRuntime = require("react/jsx-runtime");
9
10
  function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
10
11
  function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); } /**
11
12
  * External dependencies.
12
13
  */ /**
14
+ * WordPress dependencies.
15
+ */ /**
13
16
  * Internal dependencies.
14
17
  */
15
18
  var HistoryGrabber = function HistoryGrabber(_ref) {
@@ -44,6 +47,18 @@ var clickButton = /*#__PURE__*/function () {
44
47
  return _ref2.apply(this, arguments);
45
48
  };
46
49
  }();
50
+
51
+ // React formats its warnings with `%s` placeholders and passes the values as
52
+ // further arguments, so assert against the whole call rather than one argument.
53
+ var messagesFrom = function messagesFrom(spy) {
54
+ return spy.mock.calls.flat().join(' ');
55
+ };
56
+
57
+ // A failing assertion skips any `mockRestore()` left after it, which would leave
58
+ // the console swallowed for every later test in the file.
59
+ afterEach(function () {
60
+ jest.restoreAllMocks();
61
+ });
47
62
  describe('Button href with onClick', function () {
48
63
  it('awaits the onClick, then pushes the route', /*#__PURE__*/(0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator().m(function _callee3() {
49
64
  var historyRef, onClick;
@@ -234,7 +249,6 @@ describe('Button href with onClick outside a router', function () {
234
249
  })));
235
250
  case 1:
236
251
  expect(warn).toHaveBeenCalledWith(expect.stringContaining('javascript:'));
237
- warn.mockRestore();
238
252
  case 2:
239
253
  return _context10.a(2);
240
254
  }
@@ -246,22 +260,21 @@ describe('Button with a javascript: href alone', function () {
246
260
  return _react.screen.getByText('Save').closest('a');
247
261
  };
248
262
  it('renders no link', function () {
249
- var warn = jest.spyOn(console, 'warn').mockImplementation(function () {});
263
+ // Silenced only; the assertion below is about the markup, not the warning.
264
+ jest.spyOn(console, 'warn').mockImplementation(function () {});
250
265
  (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
251
266
  href: "JavaScript:alert(1)",
252
267
  children: "Save"
253
268
  }));
254
269
  expect(link()).toBeNull();
255
- warn.mockRestore();
256
270
  });
257
271
  it('sees through interleaved control characters', function () {
258
- var warn = jest.spyOn(console, 'warn').mockImplementation(function () {});
272
+ jest.spyOn(console, 'warn').mockImplementation(function () {});
259
273
  (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
260
274
  href: 'java\nscript:alert(1)',
261
275
  children: "Save"
262
276
  }));
263
277
  expect(link()).toBeNull();
264
- warn.mockRestore();
265
278
  });
266
279
  it('leaves an ordinary href alone', function () {
267
280
  (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
@@ -270,4 +283,47 @@ describe('Button with a javascript: href alone', function () {
270
283
  }));
271
284
  expect(link()).toHaveAttribute('href', '/wp-admin/admin.php?page=next');
272
285
  });
286
+ });
287
+ describe('Button loading prop', function () {
288
+ var button = function button() {
289
+ return _react.screen.getByRole('button', {
290
+ name: 'Save'
291
+ });
292
+ };
293
+ it('renders the core busy state without putting `loading` on the DOM node', function () {
294
+ var error = jest.spyOn(console, 'error').mockImplementation(function () {});
295
+ var _render = (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
296
+ children: "Save"
297
+ })),
298
+ rerender = _render.rerender;
299
+ expect(button()).not.toHaveClass('is-busy');
300
+ rerender(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
301
+ loading: true,
302
+ children: "Save"
303
+ }));
304
+ expect(button()).toHaveClass('is-busy');
305
+ // React drops an unknown boolean-valued attribute instead of writing it, so
306
+ // the warning rather than the absent attribute is what a revert trips. It is
307
+ // also deduped per property name for the life of the module registry, which
308
+ // is why this names the message instead of asserting no errors at all.
309
+ expect(messagesFrom(error)).not.toContain('non-boolean attribute');
310
+ });
311
+ });
312
+
313
+ // core's Button wraps itself in a Tooltip when it is given a `label`, and the
314
+ // Tooltip needs a ref on its child. Two call sites do that with this Button:
315
+ // the experimental-tools back button and the audience segment-group heading.
316
+ describe('Button ref', function () {
317
+ it('forwards the ref to the rendered button', function () {
318
+ var error = jest.spyOn(console, 'error').mockImplementation(function () {});
319
+ var ref = (0, _element.createRef)();
320
+ (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
321
+ ref: ref,
322
+ children: "Save"
323
+ }));
324
+ expect(ref.current).toBe(_react.screen.getByRole('button', {
325
+ name: 'Save'
326
+ }));
327
+ expect(messagesFrom(error)).not.toContain('Function components cannot be given refs');
328
+ });
273
329
  });
@@ -141,7 +141,6 @@ var CoreCard = function CoreCard(_ref) {
141
141
  }), (actions === null || actions === void 0 ? void 0 : actions.length) > 0 && actionType === 'toggle' && /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.ToggleControl, {
142
142
  className: "newspack-card--core__action",
143
143
  label: otherProps.title,
144
- hideLabelFromVision: true,
145
144
  checked: isActive,
146
145
  onChange: onToggle
147
146
  }), header && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
@@ -155,7 +154,6 @@ var CoreCard = function CoreCard(_ref) {
155
154
  }), !(actions !== null && actions !== void 0 && actions.length) > 0 && actionType === 'toggle' && /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.ToggleControl, {
156
155
  className: "newspack-card--core__action",
157
156
  label: otherProps.title,
158
- hideLabelFromVision: true,
159
157
  checked: isActive,
160
158
  onChange: onToggle
161
159
  }), (actions === null || actions === void 0 ? void 0 : actions.length) > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.DropdownMenu, {
@@ -4,7 +4,6 @@
4
4
 
5
5
  @use "~@wordpress/base-styles/colors" as wp-colors;
6
6
  @use "~@wordpress/base-styles/variables" as wp-vars;
7
- @use "../../../colors/colors.module" as colors;
8
7
 
9
8
  .newspack-card--core,
10
9
  .newspack-wizard .newspack-card--core {
@@ -45,7 +44,13 @@
45
44
  }
46
45
  .newspack-card--core {
47
46
  &__header-content {
48
- * {
47
+ h1,
48
+ h2,
49
+ h3,
50
+ h4,
51
+ h5,
52
+ h6,
53
+ p {
49
54
  line-height: wp-vars.$font-line-height-small;
50
55
  }
51
56
  }
@@ -210,9 +215,6 @@
210
215
  text-decoration: underline;
211
216
  }
212
217
  }
213
- .newspack-badge {
214
- font-weight: 400;
215
- }
216
218
  }
217
219
  }
218
220
  &__is-selectable {