newspack-components 4.3.0 → 4.4.0-epic-editor-refactor.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/DEVELOPMENT.md +868 -0
  3. package/copy-styles.js +3 -2
  4. package/dist/cjs/accordion/index.js +17 -12
  5. package/dist/cjs/action-card/index.js +176 -139
  6. package/dist/cjs/autocomplete-tokenfield/index.js +254 -234
  7. package/dist/cjs/autocomplete-with-latest-posts/index.js +172 -110
  8. package/dist/cjs/autocomplete-with-suggestions/index.js +229 -126
  9. package/dist/cjs/badge/index.js +7 -7
  10. package/dist/cjs/box-contrast/index.js +19 -15
  11. package/dist/cjs/button/index.js +41 -26
  12. package/dist/cjs/button-props/button-props.js +5 -5
  13. package/dist/cjs/button-props/index.js +2 -2
  14. package/dist/cjs/card/core-card.js +110 -52
  15. package/dist/cjs/card/index.js +74 -67
  16. package/dist/cjs/card/style-core.scss +93 -25
  17. package/dist/cjs/card/style.scss +2 -2
  18. package/dist/cjs/card-feature/index.js +141 -0
  19. package/dist/cjs/card-feature/style.scss +75 -0
  20. package/dist/cjs/card-form/index.js +144 -0
  21. package/dist/cjs/card-form/style.scss +20 -0
  22. package/dist/cjs/card-settings-group/index.js +36 -16
  23. package/dist/cjs/card-sortable-list/index.js +176 -140
  24. package/dist/cjs/category-autocomplete/index.js +175 -155
  25. package/dist/cjs/color-picker/index.js +41 -23
  26. package/dist/cjs/color-picker/index.test.js +82 -0
  27. package/dist/cjs/color-picker/style.scss +11 -13
  28. package/dist/cjs/confirm-dialog/index.js +86 -22
  29. package/dist/cjs/consts.js +1 -1
  30. package/dist/cjs/custom-select-control/index.js +39 -28
  31. package/dist/cjs/dataviews/index.js +38 -0
  32. package/dist/cjs/dataviews/style.scss +9 -0
  33. package/dist/cjs/divider/index.js +26 -21
  34. package/dist/cjs/footer/index.js +36 -30
  35. package/dist/cjs/form-token-field/index.js +51 -40
  36. package/dist/cjs/global-notices/index.js +5 -5
  37. package/dist/cjs/grid/index.js +25 -19
  38. package/dist/cjs/grid/style.scss +6 -1
  39. package/dist/cjs/handoff/index.js +204 -156
  40. package/dist/cjs/handoff-message/index.js +12 -7
  41. package/dist/cjs/hooks/index.js +10 -8
  42. package/dist/cjs/hooks/use-confirm-dialog.js +79 -0
  43. package/dist/cjs/hooks/use-unsaved-changes-dialog.js +144 -0
  44. package/dist/cjs/hooks/useObjectState.js +21 -12
  45. package/dist/cjs/hooks/useObjectState.test.js +55 -33
  46. package/dist/cjs/hooks/useOnClickOutside.js +6 -6
  47. package/dist/cjs/hooks/usePrompt.js +10 -13
  48. package/dist/cjs/image-upload/index.js +114 -123
  49. package/dist/cjs/image-upload/index.test.js +11 -13
  50. package/dist/cjs/image-upload/style.scss +12 -40
  51. package/dist/cjs/index.js +136 -104
  52. package/dist/cjs/info-button/index.js +43 -32
  53. package/dist/cjs/integration-icons/active-campaign.js +22 -0
  54. package/dist/cjs/integration-icons/constant-contact.js +28 -0
  55. package/dist/cjs/integration-icons/fundraise-up.js +25 -0
  56. package/dist/cjs/integration-icons/index.js +48 -0
  57. package/dist/cjs/integration-icons/mailchimp.js +19 -0
  58. package/dist/cjs/integration-icons/salesforce.js +17 -0
  59. package/dist/cjs/integration-icons/wisepops.js +20 -0
  60. package/dist/cjs/modal/index.js +22 -19
  61. package/dist/cjs/modal/style.scss +4 -0
  62. package/dist/cjs/newspack-icon/index.js +45 -38
  63. package/dist/cjs/notice/index.js +64 -55
  64. package/dist/cjs/plugin-installer/index.js +259 -253
  65. package/dist/cjs/plugin-settings/SettingsSection.js +66 -60
  66. package/dist/cjs/plugin-settings/index.js +217 -200
  67. package/dist/cjs/plugin-toggle/index.js +192 -199
  68. package/dist/cjs/popover/index.js +41 -28
  69. package/dist/cjs/position-control/index.js +24 -23
  70. package/dist/cjs/progress-bar/index.js +71 -59
  71. package/dist/cjs/progress-bar/index.test.js +62 -68
  72. package/dist/cjs/proxied-imports/router.js +3 -3
  73. package/dist/cjs/radio-control/index.js +40 -27
  74. package/dist/cjs/section-header/index.js +103 -58
  75. package/dist/cjs/section-header/style.scss +31 -11
  76. package/dist/cjs/select-control/ButtonGroupControl.js +26 -21
  77. package/dist/cjs/select-control/GroupedSelectControl.js +41 -35
  78. package/dist/cjs/select-control/index.js +52 -40
  79. package/dist/cjs/settings/MinMaxSetting.js +30 -20
  80. package/dist/cjs/settings/SettingsCard.js +20 -17
  81. package/dist/cjs/settings/SettingsSection.js +22 -21
  82. package/dist/cjs/settings/index.js +5 -5
  83. package/dist/cjs/sortable-newsletter-list-control/index.js +78 -57
  84. package/dist/cjs/steps-list/index.js +44 -34
  85. package/dist/cjs/steps-list-item/index.js +47 -39
  86. package/dist/cjs/style-card/index.js +70 -63
  87. package/dist/cjs/tabbed-navigation/index.js +38 -36
  88. package/dist/cjs/text-control/index.js +27 -25
  89. package/dist/cjs/utils/color.js +4 -4
  90. package/dist/cjs/utils/editor-toolbar-back-button.js +61 -0
  91. package/dist/cjs/utils/index.js +31 -19
  92. package/dist/cjs/utils/style.scss +8 -0
  93. package/dist/cjs/waiting/index.js +48 -37
  94. package/dist/cjs/web-preview/index.js +217 -211
  95. package/dist/cjs/with-wizard/index.js +362 -364
  96. package/dist/cjs/with-wizard/style.scss +4 -54
  97. package/dist/cjs/with-wizard-screen/index.js +54 -58
  98. package/dist/cjs/with-wizard-screen/style.scss +41 -3
  99. package/dist/cjs/wizard/components/WizardError.js +20 -18
  100. package/dist/cjs/wizard/components/WizardSnackbar.js +26 -23
  101. package/dist/cjs/wizard/index.js +176 -114
  102. package/dist/cjs/wizard/store/index.js +171 -123
  103. package/dist/cjs/wizard/store/utils.js +13 -8
  104. package/dist/esm/accordion/index.js +14 -9
  105. package/dist/esm/action-card/index.js +168 -123
  106. package/dist/esm/autocomplete-tokenfield/index.js +248 -222
  107. package/dist/esm/autocomplete-with-latest-posts/index.js +169 -103
  108. package/dist/esm/autocomplete-with-suggestions/index.js +226 -119
  109. package/dist/esm/badge/index.js +5 -5
  110. package/dist/esm/box-contrast/index.js +15 -11
  111. package/dist/esm/button/index.js +39 -21
  112. package/dist/esm/button-props/button-props.js +1 -1
  113. package/dist/esm/card/core-card.js +109 -47
  114. package/dist/esm/card/index.js +69 -54
  115. package/dist/esm/card/style-core.scss +93 -25
  116. package/dist/esm/card/style.scss +2 -2
  117. package/dist/esm/card-feature/index.js +131 -0
  118. package/dist/esm/card-feature/style.scss +75 -0
  119. package/dist/esm/card-form/index.js +134 -0
  120. package/dist/esm/card-form/style.scss +20 -0
  121. package/dist/esm/card-settings-group/index.js +33 -14
  122. package/dist/esm/card-sortable-list/index.js +170 -126
  123. package/dist/esm/category-autocomplete/index.js +168 -140
  124. package/dist/esm/color-picker/index.js +39 -21
  125. package/dist/esm/color-picker/index.test.js +78 -0
  126. package/dist/esm/color-picker/style.scss +11 -13
  127. package/dist/esm/confirm-dialog/index.js +85 -18
  128. package/dist/esm/consts.js +1 -1
  129. package/dist/esm/custom-select-control/index.js +34 -15
  130. package/dist/esm/dataviews/index.js +34 -0
  131. package/dist/esm/dataviews/style.scss +9 -0
  132. package/dist/esm/divider/index.js +24 -16
  133. package/dist/esm/footer/index.js +34 -28
  134. package/dist/esm/form-token-field/index.js +46 -27
  135. package/dist/esm/global-notices/index.js +3 -3
  136. package/dist/esm/grid/index.js +23 -14
  137. package/dist/esm/grid/style.scss +6 -1
  138. package/dist/esm/handoff/index.js +199 -143
  139. package/dist/esm/handoff-message/index.js +10 -6
  140. package/dist/esm/hooks/index.js +8 -6
  141. package/dist/esm/hooks/use-confirm-dialog.js +73 -0
  142. package/dist/esm/hooks/use-unsaved-changes-dialog.js +136 -0
  143. package/dist/esm/hooks/useObjectState.js +19 -9
  144. package/dist/esm/hooks/useObjectState.test.js +54 -32
  145. package/dist/esm/hooks/useOnClickOutside.js +4 -4
  146. package/dist/esm/hooks/usePrompt.js +9 -11
  147. package/dist/esm/image-upload/index.js +110 -111
  148. package/dist/esm/image-upload/index.test.js +11 -13
  149. package/dist/esm/image-upload/style.scss +12 -40
  150. package/dist/esm/index.js +7 -1
  151. package/dist/esm/info-button/index.js +38 -19
  152. package/dist/esm/integration-icons/active-campaign.js +16 -0
  153. package/dist/esm/integration-icons/constant-contact.js +22 -0
  154. package/dist/esm/integration-icons/fundraise-up.js +19 -0
  155. package/dist/esm/integration-icons/index.js +6 -0
  156. package/dist/esm/integration-icons/mailchimp.js +13 -0
  157. package/dist/esm/integration-icons/salesforce.js +11 -0
  158. package/dist/esm/integration-icons/wisepops.js +14 -0
  159. package/dist/esm/modal/index.js +20 -13
  160. package/dist/esm/modal/style.scss +4 -0
  161. package/dist/esm/newspack-icon/index.js +40 -25
  162. package/dist/esm/notice/index.js +59 -42
  163. package/dist/esm/plugin-installer/index.js +255 -243
  164. package/dist/esm/plugin-settings/SettingsSection.js +61 -50
  165. package/dist/esm/plugin-settings/index.js +212 -189
  166. package/dist/esm/plugin-toggle/index.js +187 -190
  167. package/dist/esm/popover/index.js +35 -16
  168. package/dist/esm/position-control/index.js +22 -18
  169. package/dist/esm/progress-bar/index.js +66 -49
  170. package/dist/esm/progress-bar/index.test.js +61 -67
  171. package/dist/esm/radio-control/index.js +34 -15
  172. package/dist/esm/section-header/index.js +102 -57
  173. package/dist/esm/section-header/style.scss +31 -11
  174. package/dist/esm/select-control/ButtonGroupControl.js +23 -18
  175. package/dist/esm/select-control/GroupedSelectControl.js +38 -30
  176. package/dist/esm/select-control/index.js +47 -27
  177. package/dist/esm/settings/MinMaxSetting.js +27 -16
  178. package/dist/esm/settings/SettingsCard.js +18 -13
  179. package/dist/esm/settings/SettingsSection.js +20 -19
  180. package/dist/esm/settings/index.js +3 -3
  181. package/dist/esm/sortable-newsletter-list-control/index.js +74 -53
  182. package/dist/esm/steps-list/index.js +39 -21
  183. package/dist/esm/steps-list-item/index.js +42 -26
  184. package/dist/esm/style-card/index.js +65 -50
  185. package/dist/esm/tabbed-navigation/index.js +35 -33
  186. package/dist/esm/text-control/index.js +25 -19
  187. package/dist/esm/utils/color.js +4 -4
  188. package/dist/esm/utils/editor-toolbar-back-button.js +53 -0
  189. package/dist/esm/utils/index.js +29 -16
  190. package/dist/esm/utils/style.scss +8 -0
  191. package/dist/esm/waiting/index.js +43 -24
  192. package/dist/esm/web-preview/index.js +213 -201
  193. package/dist/esm/with-wizard/index.js +360 -358
  194. package/dist/esm/with-wizard/style.scss +4 -54
  195. package/dist/esm/with-wizard-screen/index.js +48 -46
  196. package/dist/esm/with-wizard-screen/style.scss +41 -3
  197. package/dist/esm/wizard/components/WizardError.js +18 -16
  198. package/dist/esm/wizard/components/WizardSnackbar.js +23 -17
  199. package/dist/esm/wizard/index.js +171 -103
  200. package/dist/esm/wizard/store/index.js +169 -116
  201. package/dist/esm/wizard/store/utils.js +13 -6
  202. package/package.json +30 -16
  203. package/release.config.js +1 -0
  204. package/shared/hooks/use-coauthors.js +195 -27
  205. package/shared/hooks/use-coauthors.test.js +232 -6
  206. package/shared/scss/_mixins.scss +13 -5
  207. package/src/accordion/index.js +8 -7
  208. package/src/action-card/index.js +15 -1
  209. package/src/badge/index.tsx +3 -1
  210. package/src/card/core-card.js +95 -18
  211. package/src/card/index.js +1 -0
  212. package/src/card/style-core.scss +93 -25
  213. package/src/card/style.scss +2 -2
  214. package/src/card-feature/README.md +188 -0
  215. package/src/card-feature/index.tsx +186 -0
  216. package/src/card-feature/style.scss +75 -0
  217. package/src/card-form/README.md +113 -0
  218. package/src/card-form/index.tsx +128 -0
  219. package/src/card-form/style.scss +20 -0
  220. package/src/card-settings-group/index.tsx +28 -3
  221. package/src/card-sortable-list/index.tsx +27 -4
  222. package/src/color-picker/index.js +15 -8
  223. package/src/color-picker/index.test.js +52 -0
  224. package/src/color-picker/style.scss +11 -13
  225. package/src/confirm-dialog/README.md +128 -0
  226. package/src/confirm-dialog/index.tsx +74 -6
  227. package/src/dataviews/index.tsx +34 -0
  228. package/src/dataviews/style.scss +9 -0
  229. package/src/grid/style.scss +6 -1
  230. package/src/handoff/README.md +93 -0
  231. package/src/handoff/index.js +46 -8
  232. package/src/hooks/index.ts +2 -1
  233. package/src/hooks/use-confirm-dialog.tsx +80 -0
  234. package/src/hooks/use-unsaved-changes-dialog.tsx +140 -0
  235. package/src/image-upload/index.js +10 -15
  236. package/src/image-upload/style.scss +12 -40
  237. package/src/index.js +7 -1
  238. package/src/integration-icons/active-campaign.js +14 -0
  239. package/src/integration-icons/constant-contact.js +22 -0
  240. package/src/integration-icons/fundraise-up.js +18 -0
  241. package/src/integration-icons/index.js +6 -0
  242. package/src/integration-icons/mailchimp.js +10 -0
  243. package/src/integration-icons/salesforce.js +10 -0
  244. package/src/integration-icons/wisepops.js +14 -0
  245. package/src/modal/style.scss +4 -0
  246. package/src/plugin-installer/index.js +30 -19
  247. package/src/section-header/index.js +56 -24
  248. package/src/section-header/style.scss +31 -11
  249. package/src/select-control/index.js +4 -0
  250. package/src/utils/editor-toolbar-back-button.tsx +48 -0
  251. package/src/utils/index.js +6 -0
  252. package/src/utils/style.scss +8 -0
  253. package/src/with-wizard/style.scss +4 -54
  254. package/src/with-wizard-screen/style.scss +41 -3
  255. package/src/wizard/index.js +90 -53
  256. package/src/wizard/store/index.js +2 -2
  257. package/dist/cjs/button-card/index.js +0 -72
  258. package/dist/cjs/button-card/style.scss +0 -180
  259. package/dist/esm/button-card/index.js +0 -64
  260. package/dist/esm/button-card/style.scss +0 -180
  261. package/src/button-card/index.js +0 -52
  262. package/src/button-card/style.scss +0 -180
package/DEVELOPMENT.md ADDED
@@ -0,0 +1,868 @@
1
+ # Newspack Components - Development Guide
2
+
3
+ Comprehensive developer guidelines for working with the Newspack Components package. If you're building or changing a wizard or settings screen, start with [When to Use](#when-to-use) and [Design & layout at a glance](#design--layout-at-a-glance), then [Usage](#usage) and [Available Components](#available-components).
4
+
5
+ ## Contents
6
+
7
+ - [Purpose](#purpose)
8
+ - [When to Use](#when-to-use)
9
+ - [Component Selection Hierarchy](#component-selection-hierarchy)
10
+ - [Design & layout at a glance](#design--layout-at-a-glance)
11
+ - [Available Components](#available-components)
12
+ - [Usage](#usage)
13
+ - [Import Patterns](#import-patterns)
14
+ - [Common WordPress Components](#common-wordpress-components-used-alongside-newspack-components)
15
+ - [Styling](#styling)
16
+ - [Component Patterns](#component-patterns) *(for contributors)*
17
+ - [Component Development Guidelines](#component-development-guidelines)
18
+ - [Related Packages](#related-packages)
19
+ - [Testing](#testing)
20
+
21
+ ## Purpose
22
+
23
+ This package provides custom React components designed specifically for Newspack backend/admin interfaces (wizards, settings pages, etc.). These components are built on top of WordPress components and provide Newspack-specific functionality, styling, and patterns.
24
+
25
+ **In short:** For backend/admin screens use Newspack components first (Card, ActionCard, SectionHeader, etc.), fall back to WordPress components when needed, and follow the spacing scale and hierarchy patterns so UIs stay consistent. For block editor UI use WordPress components (and `AutocompleteTokenField` only when you need autocomplete). For reader-facing UI use Newspack UI or theme components, not this package.
26
+
27
+ **Design-wise:** Backend UIs should feel consistent with the WordPress admin, with clear visual hierarchy (section → card → controls) and predictable spacing. Use the same components and spacing scale across wizards so design and code stay aligned; when introducing a new pattern or layout, align with design (and designer review) before implementing.
28
+
29
+ ## When to Use
30
+
31
+ ### Decision Tree
32
+
33
+ **Backend/Admin UI (Wizards, Settings Pages):**
34
+ - ✅ **Use Newspack components first** - Check if a Newspack component exists for your use case
35
+ - ⚠️ **Fallback to WordPress components** - If no Newspack component exists, use `@wordpress/components`
36
+ - Examples: Dashboard, Settings, Audience Management, Setup Wizard
37
+
38
+ **Gutenberg Blocks:**
39
+ - ✅ **Use WordPress components** - Block editor UI should use `@wordpress/components` for consistency with the block editor
40
+ - ⚠️ **Exception:** `AutocompleteTokenField` - Can be used in blocks when autocomplete functionality is needed
41
+ - Examples: All blocks in `src/blocks/` use WordPress components
42
+
43
+ **Frontend (Reader-facing UI, including block render output):**
44
+ - ❌ **Don't use Newspack components** - Use Newspack UI (`src/newspack-ui/`) or theme components instead
45
+ - Examples: My Account, Reader Activation modals, Auth screens
46
+
47
+ ## Component Selection Hierarchy
48
+
49
+ Follow this step-by-step process when selecting a component:
50
+
51
+ 1. **Check Newspack components first**
52
+ - Review the [Available Components](#available-components) list below
53
+ - Check `packages/components/src/` directory for component implementations
54
+ - Newspack components are optimized for backend/admin workflows
55
+
56
+ 2. **If not available, use WordPress components**
57
+ - Visit [@wordpress/components Storybook](https://wordpress.github.io/gutenberg/?path=/docs/docs-introduction--page)
58
+ - Check [@wordpress/components npm package](https://www.npmjs.com/package/@wordpress/components)
59
+ - Cross-reference Storybook documentation and NPM package code with the version of `@wordpress/components` installed via `package.json` to confirm that the installed package contains the expected component(s)
60
+ - WordPress components provide standard admin UI patterns
61
+
62
+ 3. **If still not available, flag for designer review**
63
+ - Use a placeholder component and request a new component design from the design team
64
+ - Don't create new components without designer review
65
+ - Once approved, follow existing Newspack component patterns (see [Component Patterns](#component-patterns))
66
+ - Ensure it's reusable and follows WordPress design system principles
67
+ - Consider if it should be a Newspack component or a wizard-specific component
68
+
69
+ ## Design & layout at a glance
70
+
71
+ When building a screen, use the **spacing scale** (8px unit: 16, 24, 32, 48, 64) and prefer **VStack** for vertical stacks and **HStack** for related items in a row; use **Grid** for real multi-column layouts. Structure content as **SectionHeader → Card → ActionCard → controls**, with **Divider** between sections and primary actions in **`.newspack-buttons-card`**. Use the same **breakpoints** (e.g. 744px, 1128px) as existing components. Full detail: [Spacing scale](#spacing-scale-design-system), [Layout (HStack / VStack / Grid)](#layout-when-to-use-hstack-vstack-grid), [Responsive breakpoints](#responsive-breakpoints), [Visual hierarchy patterns](#visual-hierarchy-patterns), [Component states](#component-states). For code examples by context and wizard patterns, see [Usage](#usage).
72
+
73
+ ## Available Components
74
+
75
+ ### Layout Components
76
+
77
+ - **`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
+ - **`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
+ - **`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.
82
+
83
+ ### Form Components
84
+
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
+ - **`AutocompleteTokenField`** - Autocomplete token field (can be used in block editor)
92
+ - **`AutocompleteWithSuggestions`** - Autocomplete with custom suggestions
93
+ - **`AutocompleteWithLatestPosts`** - Autocomplete with latest posts
94
+ - **`CategoryAutocomplete`** - Category-specific autocomplete
95
+ - **`CustomSelectControl`** - Custom select dropdown component
96
+
97
+ ### Content Components
98
+
99
+ - **`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.
100
+ - **`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
+ - **`ProgressBar`** – Progress indicator.
103
+ - **`Accordion`** – Collapsible content sections.
104
+ - **`StepsList`** / **`StepsListItem`** – Step-by-step list components.
105
+ - **`StyleCard`** – Style preview card.
106
+
107
+ ### Wizard Components
108
+
109
+ - **`Wizard`** - Main wizard container with tabbed navigation and data fetching
110
+ - **`withWizard`** - Higher-order component for wizard screens (legacy pattern, class-based)
111
+ - Provides plugin management, error handling, loading states
112
+ - Used in older wizards like Setup Wizard
113
+ - Passes props: `wizardApiFetch`, `setError`, `isLoading`, `pluginRequirements`, etc.
114
+ - Do not use for new view components; use `Wizard` and/or `withWizardScreen` instead
115
+ - **`withWizardScreen`** - Higher-order component for wizard screens (modern pattern, function-based)
116
+ - Provides header, tabbed navigation, button actions, handoff messages
117
+ - Used in newer wizards like Audience Management
118
+ - 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
123
+
124
+ ### Plugin Management Components
125
+
126
+ - **`PluginInstaller`** - Plugin installation and activation component
127
+ - **`PluginToggle`** - Plugin enable/disable toggle
128
+ - **`PluginSettings`** - Plugin settings configuration component
129
+
130
+ ### Utility Components
131
+
132
+ - **`Modal`** - Modal dialog component
133
+ - **`Popover`** - Popover component
134
+ - **`WebPreview`** - Web preview iframe component
135
+ - **`NewspackIcon`** - Newspack icon wrapper component
136
+ - **`InfoButton`** - Info button with tooltip
137
+ - **`GlobalNotices`** - Global notice system component
138
+
139
+ ### Settings Components
140
+
141
+ - **`Settings`** - Settings container component
142
+ - **`SortableNewsletterListControl`** - Sortable newsletter list selector
143
+
144
+ ### Utilities & Hooks
145
+
146
+ - **`hooks`** - Custom React hooks (e.g., `useObjectState`, `usePrompt`, `useOnClickOutside`)
147
+ - **`utils`** - Utility functions (e.g., `confirmAction`, color utilities)
148
+ - **`Router`** - Proxied React Router import (use instead of direct `react-router-dom` import)
149
+ - Note: This package currently uses [React Router v5](https://v5.reactrouter.com/). Please refer to v5 documentation for API details.
150
+
151
+ ## Import Patterns
152
+
153
+ ### Standard Import Pattern
154
+
155
+ Follow this import order (each group separated by a blank line with a JSDoc comment):
156
+
157
+ ```jsx
158
+ /**
159
+ * External dependencies
160
+ */
161
+ import classnames from 'classnames';
162
+
163
+ /**
164
+ * WordPress dependencies
165
+ */
166
+ import { CheckboxControl, ExternalLink } from '@wordpress/components';
167
+ import { __ } from '@wordpress/i18n';
168
+ import { useState } from '@wordpress/element';
169
+
170
+ /**
171
+ * Internal dependencies
172
+ */
173
+ import {
174
+ ActionCard,
175
+ Button,
176
+ Card,
177
+ Notice,
178
+ TextControl,
179
+ } from '../../../../../packages/components/src';
180
+ ```
181
+
182
+ ### Importing from packages/components
183
+
184
+ **Within this monorepo:** Use relative paths (no webpack alias is configured):
185
+
186
+ ```jsx
187
+ // ✅ CORRECT - Within newspack-plugin monorepo
188
+ import { Button, Card, Notice } from '../../../../../packages/components/src';
189
+ ```
190
+
191
+ **As an npm package:** If `newspack-components` is installed as a dependency in another plugin, import from the package name:
192
+
193
+ ```jsx
194
+ // ✅ CORRECT - When installed as npm package
195
+ import { Button, Card, Notice } from 'newspack-components';
196
+ ```
197
+
198
+ **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`):
199
+
200
+ ```jsx
201
+ // ✅ CORRECT – alphabetical
202
+ import { Button, Card, Notice } from '../../../../../packages/components/src';
203
+
204
+ // ❌ WRONG – Don't import all
205
+ import * as Components from '../../../../../packages/components/src';
206
+ ```
207
+
208
+ ### Router Import Pattern
209
+
210
+ **Always use the proxied router** - Never import `react-router-dom` directly in source code:
211
+
212
+ ```jsx
213
+ // ✅ CORRECT
214
+ import Router from '../../../../../packages/components/src/proxied-imports/router';
215
+ const { HashRouter, Route, Switch } = Router;
216
+
217
+ // ❌ WRONG - Don't import directly
218
+ import { HashRouter } from 'react-router-dom';
219
+ ```
220
+
221
+ **Exception:** Tests may import `react-router-dom` directly.
222
+
223
+ ## Usage
224
+
225
+ This section shows how to use components **by context** (backend, blocks, frontend) and **common patterns** (Wizard, withWizardScreen, withWizard, hooks, AutocompleteTokenField in blocks). Use it together with [Available Components](#available-components) and [Import Patterns](#import-patterns).
226
+
227
+ ### Backend/Admin UI
228
+
229
+ **Rule:** Use Newspack components as the primary choice, falling back to WordPress components when needed.
230
+
231
+ **Common import pattern:**
232
+ ```jsx
233
+ /**
234
+ * WordPress dependencies
235
+ */
236
+ import { CheckboxControl, ExternalLink, RangeControl } from '@wordpress/components';
237
+
238
+ /**
239
+ * Internal dependencies
240
+ */
241
+ import {
242
+ ActionCard,
243
+ Button,
244
+ Card,
245
+ Grid,
246
+ Notice,
247
+ Divider,
248
+ SectionHeader,
249
+ TextControl,
250
+ Waiting,
251
+ } from '../../../../../packages/components/src';
252
+ ```
253
+
254
+ **Example – Audience Setup Wizard (real reference):**
255
+ ```jsx
256
+ import { CheckboxControl, ExternalLink, RangeControl } from '@wordpress/components';
257
+ import {
258
+ ActionCard,
259
+ Button,
260
+ Card,
261
+ Grid,
262
+ Notice,
263
+ Divider,
264
+ PluginInstaller,
265
+ SectionHeader,
266
+ TextControl,
267
+ Waiting,
268
+ withWizardScreen,
269
+ } from '../../../../../packages/components/src';
270
+
271
+ export default withWizardScreen( ( { config, updateConfig } ) => {
272
+ return (
273
+ <>
274
+ <Notice noticeText={ __( 'Audience Management is enabled.', 'newspack-plugin' ) } isSuccess />
275
+ <Card noBorder>
276
+ <ActionCard
277
+ title={ __( 'Present newsletter signup after checkout', 'newspack-plugin' ) }
278
+ toggleChecked={ config.use_custom_lists }
279
+ toggleOnChange={ value => updateConfig( 'use_custom_lists', value ) }
280
+ >
281
+ <Grid columns={ 4 }>
282
+ <RangeControl
283
+ label={ __( 'Initial list size', 'newspack-plugin' ) }
284
+ value={ config.newsletter_list_initial_size }
285
+ onChange={ value => updateConfig( 'newsletter_list_initial_size', parseInt( value ) ) }
286
+ />
287
+ </Grid>
288
+ </ActionCard>
289
+ </Card>
290
+ <div className="newspack-buttons-card">
291
+ <Button variant="primary" onClick={ saveConfig }>
292
+ { __( 'Save Settings', 'newspack-plugin' ) }
293
+ </Button>
294
+ </div>
295
+ </>
296
+ );
297
+ } );
298
+ ```
299
+
300
+ **Reference:** `src/wizards/audience/views/setup/setup.js`
301
+
302
+ For wizard shells and hooks see [Common patterns](#common-patterns) below.
303
+
304
+ ### Gutenberg Blocks
305
+
306
+ **Rule:** Use WordPress components for blocks. Only use `AutocompleteTokenField` from Newspack when autocomplete is needed.
307
+
308
+ **Example – Collections block:**
309
+ ```jsx
310
+ import { PanelBody, TextControl } from '@wordpress/components';
311
+ import { AutocompleteTokenField } from '../../../../packages/components/src';
312
+
313
+ function InspectorPanel( { attributes, setAttributes } ) {
314
+ return (
315
+ <PanelBody title={ __( 'Settings', 'newspack-plugin' ) }>
316
+ <TextControl
317
+ label={ __( 'Title', 'newspack-plugin' ) }
318
+ value={ attributes.title }
319
+ onChange={ value => setAttributes( { title: value } ) }
320
+ />
321
+ <AutocompleteTokenField
322
+ label={ __( 'Categories', 'newspack-plugin' ) }
323
+ value={ attributes.categories }
324
+ onChange={ value => setAttributes( { categories: value } ) }
325
+ />
326
+ </PanelBody>
327
+ );
328
+ }
329
+ ```
330
+
331
+ **Reference:** `src/blocks/collections/components/InspectorPanel.jsx`
332
+
333
+ **Why:** Blocks should match the block editor; WordPress components keep that consistency.
334
+
335
+ ### Frontend (Reader-facing UI)
336
+
337
+ **Rule:** Don’t use Newspack components. Use Newspack UI (`src/newspack-ui/`) or theme components instead.
338
+
339
+ **Why:** This package is for backend/admin only; reader-facing UI uses other systems.
340
+
341
+ ### Common patterns
342
+
343
+ #### Wizard (modern)
344
+
345
+ Use the `Wizard` component for tabbed wizard UIs with data fetching and tabbed navigation:
346
+
347
+ ```jsx
348
+ /**
349
+ * WordPress dependencies
350
+ */
351
+ import { __ } from '@wordpress/i18n';
352
+ import { Fragment } from '@wordpress/element';
353
+
354
+ /**
355
+ * Internal dependencies
356
+ */
357
+ import { GlobalNotices, Notice, Wizard } from '../../../../../packages/components/src';
358
+ import sections from './sections';
359
+
360
+ function Dashboard() {
361
+ return (
362
+ <Fragment>
363
+ <GlobalNotices />
364
+ <Wizard
365
+ headerText={ __( 'Newspack / Dashboard', 'newspack-plugin' ) }
366
+ sections={ sections }
367
+ renderAboveSections={ () => (
368
+ <>
369
+ <BrandHeader />
370
+ <SiteStatuses />
371
+ </>
372
+ ) }
373
+ />
374
+ </Fragment>
375
+ );
376
+ }
377
+ ```
378
+
379
+ ### Using withWizardScreen HOC (Modern Pattern)
380
+
381
+ Use `withWizardScreen` for modern wizard screens that need header, navigation, and button actions:
382
+
383
+ ```jsx
384
+ /**
385
+ * Internal dependencies
386
+ */
387
+ import { withWizardScreen, ActionCard, Button, Card } from '../../../../../packages/components/src';
388
+
389
+ export default withWizardScreen( ( { config, updateConfig, saveConfig, renderPrimaryButton } ) => {
390
+ return (
391
+ <Card noBorder>
392
+ <ActionCard
393
+ title={ __( 'Feature', 'newspack-plugin' ) }
394
+ toggleChecked={ config.enabled }
395
+ toggleOnChange={ value => updateConfig( 'enabled', value ) }
396
+ />
397
+ </Card>
398
+ );
399
+ } );
400
+ ```
401
+
402
+ **When to use:** Modern wizard screens (Audience Management, Settings sections, etc.). Use when you need more precise control over the layout and routing structure in a wizard view than the `Wizard` component can provide.
403
+
404
+ ### Using withWizard HOC (Legacy Pattern)
405
+
406
+ Use `withWizard` for legacy wizard screens that need plugin management and error handling:
407
+
408
+ ```jsx
409
+ /**
410
+ * Internal dependencies
411
+ */
412
+ import { withWizard, Card, PluginInstaller } from '../../../../../packages/components/src';
413
+
414
+ function MyWizardScreen( { wizardApiFetch, setError, isLoading, pluginRequirements } ) {
415
+ return (
416
+ <>
417
+ { pluginRequirements }
418
+ <Card>
419
+ {/* Wizard content */}
420
+ </Card>
421
+ </>
422
+ );
423
+ }
424
+
425
+ export default withWizard( MyWizardScreen, [ 'required-plugin-slug' ] );
426
+ ```
427
+
428
+ **When to use:** Legacy wizards (Setup Wizard, older wizard implementations). Do not use for new wizards.
429
+
430
+ ### Using Hooks and Utilities
431
+
432
+ ```jsx
433
+ /**
434
+ * Internal dependencies
435
+ */
436
+ import { hooks, utils } from '../../../../../packages/components/src';
437
+
438
+ function MyComponent() {
439
+ // useObjectState hook for managing object state
440
+ const [ data, setData ] = hooks.useObjectState( {
441
+ field1: '',
442
+ field2: false,
443
+ } );
444
+
445
+ // Update nested field
446
+ setData( { field1: 'new value' } );
447
+
448
+ // Confirm action utility
449
+ const handleDelete = () => {
450
+ if ( utils.confirmAction( __( 'Are you sure?', 'newspack-plugin' ) ) ) {
451
+ // Proceed with deletion
452
+ }
453
+ };
454
+ }
455
+ ```
456
+
457
+ ### Using AutocompleteTokenField in the Block Editor
458
+
459
+ ```jsx
460
+ /**
461
+ * WordPress dependencies
462
+ */
463
+ import { PanelBody } from '@wordpress/components';
464
+
465
+ /**
466
+ * Internal dependencies
467
+ */
468
+ import { AutocompleteTokenField } from '../../../../packages/components/src';
469
+
470
+ function InspectorPanel( { attributes, setAttributes } ) {
471
+ return (
472
+ <PanelBody>
473
+ <AutocompleteTokenField
474
+ label={ __( 'Categories', 'newspack-plugin' ) }
475
+ value={ attributes.categories }
476
+ onChange={ value => setAttributes( { categories: value } ) }
477
+ suggestions={ [ 'News', 'Opinion', 'Sports' ] }
478
+ />
479
+ </PanelBody>
480
+ );
481
+ }
482
+ ```
483
+
484
+ ## Common WordPress Components Used Alongside Newspack Components
485
+
486
+ When Newspack components don't provide what you need, use these WordPress components. The list below reflects actual usage across the codebase (wizards, blocks, packages/components, other scripts).
487
+
488
+ ### Form controls
489
+
490
+ - **`CheckboxControl`** – Checkbox input (very common in wizards and blocks)
491
+ - **`ToggleControl`** – Toggle switch (common in settings and content gates)
492
+ - **`RangeControl`** – Numeric range slider
493
+ - **`TextControl`** – Single-line text input
494
+ - **`TextareaControl`** – Multi-line text input
495
+ - **`SelectControl`** – Select dropdown
496
+ - **`FormTokenField`** – Core token/tag input (e.g. categories, tags); for wizards and settings UIs prefer the Newspack `FormTokenField` wrapper, which adds a `description` prop for help text like other controls.
497
+ - **`__experimentalNumberControl as NumberControl`** – Number input (use with `@wordpress/no-unsafe-wp-apis` eslint comment if needed)
498
+ - **`__experimentalToggleGroupControl`** / **`__experimentalToggleGroupControlOption`** – Toggle group (e.g. content gifting, countdown banner, contribution meter; use with eslint comment for unsafe APIs)
499
+
500
+ ### Layout
501
+
502
+ - **`__experimentalHStack as HStack`** – Horizontal stack (in use, e.g. Audience > Subscriptions)
503
+ - **`__experimentalVStack as VStack`** – Vertical stack (preferred for new layout when you need vertical stacking; use same import pattern as HStack)
504
+ - **`Flex`** / **`FlexItem`** – Flex layout (used in Nextdoor sidebar; consider HStack/VStack for new code when appropriate)
505
+
506
+ ### Panels and structure (blocks and admin)
507
+
508
+ - **`Panel`** / **`PanelBody`** / **`PanelHeader`** / **`PanelRow`** – Block editor panels and rows
509
+ - **`BaseControl`** – Base wrapper for form controls (label + help text)
510
+ - **`useBaseControlProps`** – Hook for base control props (e.g. collection meta CTAs)
511
+ - **`CardHeader`** / **`CardBody`** – Card layout (e.g. Nextdoor settings)
512
+ - **`__experimentalHeading as Heading`** – Heading component (use with eslint comment for unsafe APIs when needed)
513
+
514
+ ### Buttons, links, menus
515
+
516
+ - **`Button`** – Button (blocks and some admin; prefer Newspack `Button` in wizards)
517
+ - **`ExternalLink`** – Link that opens in a new tab (very common for docs/help)
518
+ - **`MenuItem`** – Item inside dropdown/menu
519
+ - **`DropdownMenu`** – Dropdown menu (e.g. content gates, webhooks, ad units)
520
+
521
+ ### Feedback and overlays
522
+
523
+ - **`Spinner`** – Loading spinner
524
+ - **`Notice`** – Inline notice (success/error/warning); prefer Newspack `Notice` in wizards when it fits
525
+ - **`Placeholder`** – Empty state in blocks
526
+ - **`Modal`** – Modal dialog
527
+ - **`Popover`** – Popover (e.g. webhooks endpoint actions, corrections modal)
528
+ - **`Tooltip`** – Tooltip (e.g. site statuses, info)
529
+
530
+ ### Block editor UI
531
+
532
+ - **`ToolbarButton`** / **`ToolbarGroup`** – Block toolbar buttons and groups
533
+ - **`Icon`** – Icon wrapper (from `@wordpress/icons`; pass an icon from `@wordpress/icons` or newspack-icons)
534
+
535
+ ### Other
536
+
537
+ - **`Draggable`** – Drag-and-drop reorder (e.g. ActionCard, collection meta CTAs)
538
+ - **`DatePicker`** / **`DateTimePicker`** – Date/time picker (e.g. contribution meter, corrections modal)
539
+ - **`ClipboardButton`** – Copy-to-clipboard button (e.g. Salesforce)
540
+ - **`SVG`** / **`Path`** – Inline SVG (e.g. NewspackIcon, Nextdoor)
541
+
542
+ **Icons:** Do not use Dashicons. Use **`@wordpress/icons`** first, then **newspack-icons** ([packages/icons](../icons/DEVELOPMENT.md)). Use the `Icon` component from `@wordpress/icons` with an icon from either library. See the [Icons Development Guide](../icons/DEVELOPMENT.md) for the full selection hierarchy.
543
+
544
+ ### Importing experimental components
545
+
546
+ Experimental components (e.g. `HStack`, `VStack`, `NumberControl`, `ToggleGroupControl`, `Heading`) live under `@wordpress/components` and may be prefixed with `__experimental`. Use the same import style as the rest of the codebase and add an eslint disable for unsafe APIs when required:
547
+
548
+ ```jsx
549
+ import {
550
+ __experimentalHStack as HStack, // eslint-disable-line @wordpress/no-unsafe-wp-apis
551
+ __experimentalNumberControl as NumberControl, // eslint-disable-line @wordpress/no-unsafe-wp-apis
552
+ __experimentalVStack as VStack, // eslint-disable-line @wordpress/no-unsafe-wp-apis
553
+ ExternalLink,
554
+ } from '@wordpress/components';
555
+ ```
556
+
557
+ **Example – form controls and layout:**
558
+ ```jsx
559
+ import {
560
+ CheckboxControl,
561
+ ExternalLink,
562
+ __experimentalHStack as HStack, // eslint-disable-line @wordpress/no-unsafe-wp-apis
563
+ RangeControl,
564
+ } from '@wordpress/components';
565
+ import { ActionCard, Card } from '../../../../../packages/components/src';
566
+
567
+ <ActionCard title="Settings">
568
+ <HStack>
569
+ <RangeControl
570
+ label={ __( 'Value', 'newspack-plugin' ) }
571
+ value={ config.value }
572
+ onChange={ value => updateConfig( 'value', value ) }
573
+ />
574
+ <CheckboxControl
575
+ label={ __( 'Enable', 'newspack-plugin' ) }
576
+ checked={ config.enabled }
577
+ onChange={ value => updateConfig( 'enabled', value ) }
578
+ />
579
+ </HStack>
580
+ </ActionCard>
581
+ ```
582
+
583
+ ## Styling
584
+
585
+ Newspack components use SCSS with BEM-ish naming conventions and a consistent spacing scale so layouts stay visually consistent.
586
+
587
+ ### Naming and structure
588
+
589
+ - **Prefix:** `newspack-` (e.g. `.newspack-card`, `.newspack-button`)
590
+ - **Modifiers:** Use `--` for modifiers (e.g. `.newspack-card--no-border`)
591
+ - **Elements**: Use `__` for elements that are part of a larger block-level component (e.g. `.newspack-card__header-content`)
592
+ - **WordPress colors:** Use WordPress design system colors (see [Colors Development Guide](../colors/DEVELOPMENT.md))
593
+ - **Custom styles:** Component-specific styles live in `packages/components/src/{component}/style.scss`
594
+
595
+ ### Spacing scale (design system)
596
+
597
+ Spacing is based on an **8px unit**. Use these values so new styles match existing components:
598
+
599
+ | Value | Use in components | Typical use |
600
+ |-------|-------------------|-------------|
601
+ | **8px** | Tight gaps, badge padding, small padding (e.g. ActionCard is-small) | Inline or dense UI |
602
+ | **16px** | Gaps between related controls, buttons card gap, margins inside ActionCard region-children for Card/Grid/TextControl | Related items, form rows |
603
+ | **24px** | Default ActionCard region padding, toggle/region gaps, expandable content padding and sibling spacing | Default internal padding and gaps within a card |
604
+ | **32px** | Card vertical margin, Grid default gap and margin, SectionHeader first-child top, Notice margin, Divider margin | Section rhythm, between blocks |
605
+ | **48px** | SectionHeader container margin-top, Card horizontal padding (small screens) | Section separation |
606
+ | **64px** | SectionHeader top margin, Divider margins (large breakpoint), buttons card margin, Card horizontal padding (large screens) | Major section separation |
607
+
608
+ **In code:** Card uses `margin: 32px 0` and `padding: 16px 48px` (32px 64px at 744px+). ActionCard uses 24px for region padding and 24px between regions; region-children use `padding: 0 24px 24px` (0 32px 32px for is-medium). Grid uses `grid-gap: 32px` and `margin: 32px 0` by default, with optional gutter classes (`__gutter-8`, `__gutter-16`, etc.). When adding new components or overrides, prefer these values (or 8px multiples) instead of ad-hoc spacing.
609
+
610
+ ### Layout: when to use HStack, VStack, Grid
611
+
612
+ - **HStack** – A few related items in a row (e.g. label + control, or two related controls). Use when the relationship is “these belong together horizontally.”
613
+ - **VStack** – Stack blocks vertically with consistent spacing. **Prefer VStack** for a single column of items, lists of settings, or stacked sections when you want vertical rhythm without custom margins. For new code, favour VStack over using Grid as a single row.
614
+ - **Grid (Newspack)** – A set of items in columns (e.g. multiple cards or form groups). Default 32px gap; use `columns` and gutter modifiers. Use when the layout is genuinely a grid of items (multiple columns). There are many existing examples where Grid is used as a single row—that’s not wrong, but we’d use VStack (or HStack) for that in new code and may update those over time.
615
+
616
+ Prefer these patterns (and the spacing scale above) over one-off margins so layout stays consistent with Card, ActionCard, and SectionHeader.
617
+
618
+ ### Responsive breakpoints
619
+
620
+ Components use consistent breakpoints so layouts behave predictably across viewports. Use these when adding or changing responsive styles:
621
+
622
+ | Breakpoint | Typical use |
623
+ |------------|-------------|
624
+ | **600px** | Narrow viewport overrides (e.g. wizard layout) |
625
+ | **744px** | Card padding, ActionCard region layout (title + toggle side-by-side), Grid columns (2–3), modal, withWizardScreen layout |
626
+ | **783px** | Wizard content width, Divider full margin |
627
+ | **960px** | Modal width, wizard layout |
628
+ | **961px** | Wizard layout |
629
+ | **1128px** | Grid 3–4 columns |
630
+ | **1224px** | Large wizard layout |
631
+
632
+ Prefer these values over new breakpoints so behaviour stays consistent with Card, Grid, and wizard shells.
633
+
634
+ ### Visual hierarchy patterns
635
+
636
+ Common composition patterns keep screens predictable. Use these as a reference when building new wizards or settings:
637
+
638
+ **Settings screen (Wizard-based):**
639
+ ```
640
+ Wizard
641
+ GlobalNotices
642
+ SectionHeader ← 64px top margin
643
+ Card ← 32px margin
644
+ ActionCard ← 24px padding, optional toggle
645
+ Grid or VStack ← 32px gap
646
+ TextControl / SelectControl / etc.
647
+ Divider ← 32px / 64px margin
648
+ ActionCard
649
+ .newspack-buttons-card ← 64px margin, 16px gap
650
+ Button (primary)
651
+ ```
652
+
653
+ **Wizard step (withWizardScreen):**
654
+ ```
655
+ withWizardScreen
656
+ Notice (optional)
657
+ Card noBorder
658
+ ActionCard (toggle + description)
659
+ [Expandable content when enabled]
660
+ Divider
661
+ ActionCard
662
+ .newspack-buttons-card
663
+ Button
664
+ ```
665
+
666
+ **Form group:** Use `Grid columns={4}` (or VStack) with TextControl, SelectControl, CheckboxControl, etc. inside a Card or ActionCard; spacing between controls follows the [spacing scale](#spacing-scale-design-system) (e.g. 16px for related rows).
667
+
668
+ ### Component states
669
+
670
+ Components rely on WordPress design system states where applicable; a few Newspack-specific behaviours:
671
+
672
+ - **ActionCard (clickable)** – Hover: `box-shadow: 0 4px 8px rgba(black, 0.08)`; transition 125ms ease-in-out. Use for cards that navigate or open.
673
+ - **Button** – Primary, secondary, disabled, and link variants follow `@wordpress/components` Button; focus and hover come from WordPress base styles.
674
+ - **Toggle (inside ActionCard)** – Checked/unchecked and focus states from WordPress ToggleControl; label is visually hidden but available for accessibility.
675
+ - **Notice** – Success (green), error (red), warning (yellow), info (gray) variants; use for feedback only and one primary message per area when possible.
676
+
677
+ When adding new interactive components, preserve focus visibility and use the same state patterns (hover shadow, transition) so the UI feels consistent.
678
+
679
+ ### Example
680
+
681
+ ```scss
682
+ .newspack-card {
683
+ border-radius: 2px;
684
+ border: 1px solid wp-colors.$gray-300;
685
+ margin: 32px 0;
686
+ padding: 16px 48px;
687
+
688
+ @media screen and (min-width: 744px) {
689
+ padding: 32px 64px;
690
+ }
691
+
692
+ &--no-border {
693
+ border: 0;
694
+ padding: 0;
695
+ }
696
+ }
697
+ ```
698
+
699
+ ## Related Packages
700
+
701
+ - **[newspack-colors](../colors/)** – Color palette used by components (via WordPress design system)
702
+ - **[newspack-icons](../icons/)** – Custom icons used in components
703
+ - **[@wordpress/components](https://www.npmjs.com/package/@wordpress/components)** – WordPress component library (fallback when Newspack components don’t exist)
704
+ - **[@wordpress/base-styles](https://www.npmjs.com/package/@wordpress/base-styles)** – WordPress Design System styles and colors
705
+
706
+ **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.
707
+
708
+ ## Component Patterns
709
+
710
+ *For contributors adding or changing components.*
711
+
712
+ ### Wrapping WordPress Components
713
+
714
+ Many Newspack components wrap WordPress components to add Newspack-specific functionality:
715
+
716
+ **Example - Button Component:**
717
+ ```tsx
718
+ import { Button as BaseComponent } from '@wordpress/components';
719
+ import Router from '../proxied-imports/router';
720
+
721
+ const Button = ( { href, onClick, ...otherProps } ) => {
722
+ const history = useHistory();
723
+ // Newspack-specific logic: handle both href and onClick
724
+ if ( href && onClick ) {
725
+ // Await onClick then redirect
726
+ }
727
+ return <BaseComponent { ...otherProps } />;
728
+ };
729
+ ```
730
+
731
+ **Example - TextControl Component:**
732
+ ```tsx
733
+ import { TextControl as BaseComponent } from '@wordpress/components';
734
+
735
+ const TextControl = ( { required, isWide, ...otherProps } ) => {
736
+ // Newspack-specific styling and required field handling
737
+ return (
738
+ <div className="newspack-text-control--required">
739
+ <BaseComponent className={ classes } required={ required } { ...otherProps } />
740
+ </div>
741
+ );
742
+ };
743
+ ```
744
+
745
+ ### Standalone Components
746
+
747
+ Some components are built from scratch for Newspack-specific use cases:
748
+
749
+ **Example - ActionCard Component:**
750
+ ```jsx
751
+ // Custom component with toggle, actions, expandable content, etc.
752
+ const ActionCard = ( {
753
+ title,
754
+ description,
755
+ toggleChecked,
756
+ toggleOnChange,
757
+ children,
758
+ // ... many other props
759
+ } ) => {
760
+ // Custom implementation
761
+ };
762
+ ```
763
+
764
+ ### Functional vs Class Components
765
+
766
+ Modern React APIs prefer functional components over class components, even though both are currently still supported. Some older Newspack components are still class-based, while newer components are functional. When creating new Newspack components, use functional components instead of class components.
767
+
768
+ **Example - Functional component (correct):**
769
+ ```jsx
770
+ // ✅ CORRECT - example from the Divider component
771
+ /**
772
+ * Divider
773
+ */
774
+
775
+ /**
776
+ * Internal dependencies
777
+ */
778
+ import './style.scss';
779
+
780
+ /**
781
+ * External dependencies
782
+ */
783
+ import classNames from 'classnames';
784
+
785
+ const Divider = ( { alignment = 'none', className = undefined, marginBottom = 64, marginTop = 64, variant = 'default', ...otherProps } ) => {
786
+ const classes = classNames(
787
+ 'newspack-divider',
788
+ className,
789
+ alignment && `newspack-divider--alignment-${ alignment }`,
790
+ variant && `newspack-divider--variant-${ variant }`
791
+ );
792
+
793
+ const style = {
794
+ '--divider-margin-bottom': typeof marginBottom === 'number' ? `${ marginBottom }px` : marginBottom,
795
+ '--divider-margin-top': typeof marginTop === 'number' ? `${ marginTop }px` : marginTop,
796
+ };
797
+
798
+ return <hr className={ classes } style={ style } { ...otherProps } />;
799
+ };
800
+
801
+ export default Divider;
802
+ ```
803
+
804
+ **Example - Class component (avoid in new code):**
805
+ ```jsx
806
+ // ❌ WRONG – Example from the older `Popover` component
807
+ /**
808
+ * WordPress dependencies
809
+ */
810
+ import { Popover as BaseComponent } from '@wordpress/components';
811
+ import { Component } from '@wordpress/element';
812
+
813
+ /**
814
+ * Internal dependencies
815
+ */
816
+ import './style.scss';
817
+
818
+ /**
819
+ * External dependencies
820
+ */
821
+ import classnames from 'classnames';
822
+
823
+ /**
824
+ * Popover
825
+ */
826
+ class Popover extends Component {
827
+ /**
828
+ * Render
829
+ */
830
+ render() {
831
+ const { className, padding, ...otherProps } = this.props;
832
+ const classes = classnames( 'newspack-popover', padding && 'newspack-popover__padding-' + padding, className );
833
+ return <BaseComponent className={ classes } { ...otherProps } />;
834
+ }
835
+ }
836
+
837
+ Popover.defaultProps = {
838
+ padding: false,
839
+ };
840
+
841
+ export default Popover;
842
+ ```
843
+
844
+ ## Component Development Guidelines
845
+
846
+ When creating new components:
847
+
848
+ 1. **Check if WordPress component exists** – Consider wrapping/extending WordPress components first.
849
+ 2. **Align with design** – For new patterns or layouts (e.g. a new card style or wizard step), get designer review before implementing so spacing, hierarchy, and component choice match the design system.
850
+ 3. **Follow import patterns** – Use the standard import order with JSDoc comments.
851
+ 4. **Translate static strings** – Always wrap user-facing text in translation functions (`__()`, `_e()`, `_n()`, etc.) from `@wordpress/i18n` with the `'newspack-plugin'` text domain. This includes labels, button text, error messages, help text, and any other strings displayed to users.
852
+ 5. **Prefer functional components** – Use functional components instead of class components for new components. See [Functional vs Class Components](#functional-vs-class-components) for examples.
853
+ 6. **Use TypeScript** – Prefer `.tsx` for new components (codebase is migrating to TypeScript).
854
+ 7. **Add PropTypes or TypeScript types** – Document component props.
855
+ 8. **Include styles** – Add component-specific styles in `style.scss`; use the [spacing scale](#spacing-scale-design-system) (8px multiples: 16, 24, 32, 48, 64) so new components match Card, ActionCard, and Grid.
856
+ 9. **Follow naming conventions** – Use BEM-ish naming with `newspack-` prefix.
857
+ 10. **Use WordPress design system** – Leverage WordPress colors and the same spacing values as existing components.
858
+ 11. **Export from index.js** – Add component to `packages/components/src/index.js`.
859
+ 12. **Document usage** – Add JSDoc comments and update this guide.
860
+ 13. **Components Demo** - If the component is complex or would benefit from demo examples, add it to the [Components Demo page](#testing).
861
+
862
+ ## Testing
863
+
864
+ Components can be tested using the Components Demo page:
865
+
866
+ - **URL:** `/wp-admin/admin.php?page=newspack-components-demo`
867
+ - **Location:** `src/wizards/componentsDemo/index.js`
868
+ - **Purpose:** Visual testing and documentation of component usage