newspack-components 4.3.0 → 4.4.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/DEVELOPMENT.md +868 -0
  3. package/colors/colors.module.scss +52 -2
  4. package/copy-styles.js +3 -2
  5. package/dist/cjs/accordion/index.js +17 -12
  6. package/dist/cjs/action-card/index.js +176 -139
  7. package/dist/cjs/autocomplete-tokenfield/index.js +254 -234
  8. package/dist/cjs/autocomplete-with-latest-posts/index.js +172 -110
  9. package/dist/cjs/autocomplete-with-suggestions/index.js +229 -126
  10. package/dist/cjs/badge/index.js +7 -7
  11. package/dist/cjs/box-contrast/index.js +19 -15
  12. package/dist/cjs/button/index.js +41 -26
  13. package/dist/cjs/button-props/button-props.js +5 -5
  14. package/dist/cjs/button-props/index.js +2 -2
  15. package/dist/cjs/card/core-card.js +110 -52
  16. package/dist/cjs/card/index.js +74 -67
  17. package/dist/cjs/card/style-core.scss +93 -25
  18. package/dist/cjs/card/style.scss +2 -2
  19. package/dist/cjs/card-feature/index.js +141 -0
  20. package/dist/cjs/card-feature/style.scss +75 -0
  21. package/dist/cjs/card-form/index.js +144 -0
  22. package/dist/cjs/card-form/style.scss +20 -0
  23. package/dist/cjs/card-settings-group/index.js +36 -16
  24. package/dist/cjs/card-sortable-list/index.js +176 -140
  25. package/dist/cjs/category-autocomplete/index.js +175 -155
  26. package/dist/cjs/color-picker/index.js +41 -23
  27. package/dist/cjs/color-picker/index.test.js +82 -0
  28. package/dist/cjs/color-picker/style.scss +11 -13
  29. package/dist/cjs/confirm-dialog/index.js +86 -22
  30. package/dist/cjs/consts.js +1 -1
  31. package/dist/cjs/custom-select-control/index.js +39 -28
  32. package/dist/cjs/dataviews/index.js +38 -0
  33. package/dist/cjs/dataviews/style.scss +9 -0
  34. package/dist/cjs/divider/index.js +26 -21
  35. package/dist/cjs/footer/index.js +36 -30
  36. package/dist/cjs/form-token-field/index.js +51 -40
  37. package/dist/cjs/global-notices/index.js +5 -5
  38. package/dist/cjs/grid/index.js +25 -19
  39. package/dist/cjs/grid/style.scss +6 -1
  40. package/dist/cjs/handoff/index.js +204 -156
  41. package/dist/cjs/handoff-message/index.js +12 -7
  42. package/dist/cjs/hooks/index.js +10 -8
  43. package/dist/cjs/hooks/use-confirm-dialog.js +79 -0
  44. package/dist/cjs/hooks/use-unsaved-changes-dialog.js +144 -0
  45. package/dist/cjs/hooks/useObjectState.js +21 -12
  46. package/dist/cjs/hooks/useObjectState.test.js +55 -33
  47. package/dist/cjs/hooks/useOnClickOutside.js +6 -6
  48. package/dist/cjs/hooks/usePrompt.js +10 -13
  49. package/dist/cjs/image-upload/index.js +114 -123
  50. package/dist/cjs/image-upload/index.test.js +11 -13
  51. package/dist/cjs/image-upload/style.scss +12 -40
  52. package/dist/cjs/index.js +136 -104
  53. package/dist/cjs/info-button/index.js +43 -32
  54. package/dist/cjs/integration-icons/active-campaign.js +22 -0
  55. package/dist/cjs/integration-icons/constant-contact.js +28 -0
  56. package/dist/cjs/integration-icons/fundraise-up.js +25 -0
  57. package/dist/cjs/integration-icons/index.js +48 -0
  58. package/dist/cjs/integration-icons/mailchimp.js +19 -0
  59. package/dist/cjs/integration-icons/salesforce.js +17 -0
  60. package/dist/cjs/integration-icons/wisepops.js +20 -0
  61. package/dist/cjs/modal/index.js +22 -19
  62. package/dist/cjs/modal/style.scss +4 -0
  63. package/dist/cjs/newspack-icon/index.js +45 -38
  64. package/dist/cjs/notice/index.js +64 -55
  65. package/dist/cjs/plugin-installer/index.js +259 -253
  66. package/dist/cjs/plugin-settings/SettingsSection.js +66 -60
  67. package/dist/cjs/plugin-settings/index.js +217 -200
  68. package/dist/cjs/plugin-toggle/index.js +192 -199
  69. package/dist/cjs/popover/index.js +41 -28
  70. package/dist/cjs/position-control/index.js +24 -23
  71. package/dist/cjs/progress-bar/index.js +71 -59
  72. package/dist/cjs/progress-bar/index.test.js +62 -68
  73. package/dist/cjs/proxied-imports/router.js +3 -3
  74. package/dist/cjs/radio-control/index.js +40 -27
  75. package/dist/cjs/section-header/index.js +103 -58
  76. package/dist/cjs/section-header/style.scss +31 -11
  77. package/dist/cjs/select-control/ButtonGroupControl.js +26 -21
  78. package/dist/cjs/select-control/GroupedSelectControl.js +41 -35
  79. package/dist/cjs/select-control/index.js +52 -40
  80. package/dist/cjs/settings/MinMaxSetting.js +30 -20
  81. package/dist/cjs/settings/SettingsCard.js +20 -17
  82. package/dist/cjs/settings/SettingsSection.js +22 -21
  83. package/dist/cjs/settings/index.js +5 -5
  84. package/dist/cjs/sortable-newsletter-list-control/index.js +78 -57
  85. package/dist/cjs/steps-list/index.js +44 -34
  86. package/dist/cjs/steps-list-item/index.js +47 -39
  87. package/dist/cjs/style-card/index.js +70 -63
  88. package/dist/cjs/tabbed-navigation/index.js +38 -36
  89. package/dist/cjs/text-control/index.js +27 -25
  90. package/dist/cjs/utils/color.js +4 -4
  91. package/dist/cjs/utils/editor-toolbar-back-button.js +61 -0
  92. package/dist/cjs/utils/index.js +31 -19
  93. package/dist/cjs/utils/style.scss +8 -0
  94. package/dist/cjs/waiting/index.js +48 -37
  95. package/dist/cjs/web-preview/index.js +217 -211
  96. package/dist/cjs/with-wizard/index.js +362 -364
  97. package/dist/cjs/with-wizard/style.scss +4 -54
  98. package/dist/cjs/with-wizard-screen/index.js +54 -58
  99. package/dist/cjs/with-wizard-screen/style.scss +41 -3
  100. package/dist/cjs/wizard/components/WizardError.js +20 -18
  101. package/dist/cjs/wizard/components/WizardSnackbar.js +26 -23
  102. package/dist/cjs/wizard/index.js +176 -114
  103. package/dist/cjs/wizard/store/index.js +171 -123
  104. package/dist/cjs/wizard/store/utils.js +13 -8
  105. package/dist/esm/accordion/index.js +14 -9
  106. package/dist/esm/action-card/index.js +168 -123
  107. package/dist/esm/autocomplete-tokenfield/index.js +248 -222
  108. package/dist/esm/autocomplete-with-latest-posts/index.js +169 -103
  109. package/dist/esm/autocomplete-with-suggestions/index.js +226 -119
  110. package/dist/esm/badge/index.js +5 -5
  111. package/dist/esm/box-contrast/index.js +15 -11
  112. package/dist/esm/button/index.js +39 -21
  113. package/dist/esm/button-props/button-props.js +1 -1
  114. package/dist/esm/card/core-card.js +109 -47
  115. package/dist/esm/card/index.js +69 -54
  116. package/dist/esm/card/style-core.scss +93 -25
  117. package/dist/esm/card/style.scss +2 -2
  118. package/dist/esm/card-feature/index.js +131 -0
  119. package/dist/esm/card-feature/style.scss +75 -0
  120. package/dist/esm/card-form/index.js +134 -0
  121. package/dist/esm/card-form/style.scss +20 -0
  122. package/dist/esm/card-settings-group/index.js +33 -14
  123. package/dist/esm/card-sortable-list/index.js +170 -126
  124. package/dist/esm/category-autocomplete/index.js +168 -140
  125. package/dist/esm/color-picker/index.js +39 -21
  126. package/dist/esm/color-picker/index.test.js +78 -0
  127. package/dist/esm/color-picker/style.scss +11 -13
  128. package/dist/esm/confirm-dialog/index.js +85 -18
  129. package/dist/esm/consts.js +1 -1
  130. package/dist/esm/custom-select-control/index.js +34 -15
  131. package/dist/esm/dataviews/index.js +34 -0
  132. package/dist/esm/dataviews/style.scss +9 -0
  133. package/dist/esm/divider/index.js +24 -16
  134. package/dist/esm/footer/index.js +34 -28
  135. package/dist/esm/form-token-field/index.js +46 -27
  136. package/dist/esm/global-notices/index.js +3 -3
  137. package/dist/esm/grid/index.js +23 -14
  138. package/dist/esm/grid/style.scss +6 -1
  139. package/dist/esm/handoff/index.js +199 -143
  140. package/dist/esm/handoff-message/index.js +10 -6
  141. package/dist/esm/hooks/index.js +8 -6
  142. package/dist/esm/hooks/use-confirm-dialog.js +73 -0
  143. package/dist/esm/hooks/use-unsaved-changes-dialog.js +136 -0
  144. package/dist/esm/hooks/useObjectState.js +19 -9
  145. package/dist/esm/hooks/useObjectState.test.js +54 -32
  146. package/dist/esm/hooks/useOnClickOutside.js +4 -4
  147. package/dist/esm/hooks/usePrompt.js +9 -11
  148. package/dist/esm/image-upload/index.js +110 -111
  149. package/dist/esm/image-upload/index.test.js +11 -13
  150. package/dist/esm/image-upload/style.scss +12 -40
  151. package/dist/esm/index.js +7 -1
  152. package/dist/esm/info-button/index.js +38 -19
  153. package/dist/esm/integration-icons/active-campaign.js +16 -0
  154. package/dist/esm/integration-icons/constant-contact.js +22 -0
  155. package/dist/esm/integration-icons/fundraise-up.js +19 -0
  156. package/dist/esm/integration-icons/index.js +6 -0
  157. package/dist/esm/integration-icons/mailchimp.js +13 -0
  158. package/dist/esm/integration-icons/salesforce.js +11 -0
  159. package/dist/esm/integration-icons/wisepops.js +14 -0
  160. package/dist/esm/modal/index.js +20 -13
  161. package/dist/esm/modal/style.scss +4 -0
  162. package/dist/esm/newspack-icon/index.js +40 -25
  163. package/dist/esm/notice/index.js +59 -42
  164. package/dist/esm/plugin-installer/index.js +255 -243
  165. package/dist/esm/plugin-settings/SettingsSection.js +61 -50
  166. package/dist/esm/plugin-settings/index.js +212 -189
  167. package/dist/esm/plugin-toggle/index.js +187 -190
  168. package/dist/esm/popover/index.js +35 -16
  169. package/dist/esm/position-control/index.js +22 -18
  170. package/dist/esm/progress-bar/index.js +66 -49
  171. package/dist/esm/progress-bar/index.test.js +61 -67
  172. package/dist/esm/radio-control/index.js +34 -15
  173. package/dist/esm/section-header/index.js +102 -57
  174. package/dist/esm/section-header/style.scss +31 -11
  175. package/dist/esm/select-control/ButtonGroupControl.js +23 -18
  176. package/dist/esm/select-control/GroupedSelectControl.js +38 -30
  177. package/dist/esm/select-control/index.js +47 -27
  178. package/dist/esm/settings/MinMaxSetting.js +27 -16
  179. package/dist/esm/settings/SettingsCard.js +18 -13
  180. package/dist/esm/settings/SettingsSection.js +20 -19
  181. package/dist/esm/settings/index.js +3 -3
  182. package/dist/esm/sortable-newsletter-list-control/index.js +74 -53
  183. package/dist/esm/steps-list/index.js +39 -21
  184. package/dist/esm/steps-list-item/index.js +42 -26
  185. package/dist/esm/style-card/index.js +65 -50
  186. package/dist/esm/tabbed-navigation/index.js +35 -33
  187. package/dist/esm/text-control/index.js +25 -19
  188. package/dist/esm/utils/color.js +4 -4
  189. package/dist/esm/utils/editor-toolbar-back-button.js +53 -0
  190. package/dist/esm/utils/index.js +29 -16
  191. package/dist/esm/utils/style.scss +8 -0
  192. package/dist/esm/waiting/index.js +43 -24
  193. package/dist/esm/web-preview/index.js +213 -201
  194. package/dist/esm/with-wizard/index.js +360 -358
  195. package/dist/esm/with-wizard/style.scss +4 -54
  196. package/dist/esm/with-wizard-screen/index.js +48 -46
  197. package/dist/esm/with-wizard-screen/style.scss +41 -3
  198. package/dist/esm/wizard/components/WizardError.js +18 -16
  199. package/dist/esm/wizard/components/WizardSnackbar.js +23 -17
  200. package/dist/esm/wizard/index.js +171 -103
  201. package/dist/esm/wizard/store/index.js +169 -116
  202. package/dist/esm/wizard/store/utils.js +13 -6
  203. package/package.json +30 -16
  204. package/release.config.js +1 -0
  205. package/shared/hooks/use-coauthors.js +195 -27
  206. package/shared/hooks/use-coauthors.test.js +232 -6
  207. package/shared/scss/_mixins.scss +13 -5
  208. package/src/accordion/index.js +8 -7
  209. package/src/action-card/index.js +15 -1
  210. package/src/badge/index.tsx +3 -1
  211. package/src/card/core-card.js +95 -18
  212. package/src/card/index.js +1 -0
  213. package/src/card/style-core.scss +93 -25
  214. package/src/card/style.scss +2 -2
  215. package/src/card-feature/README.md +188 -0
  216. package/src/card-feature/index.tsx +186 -0
  217. package/src/card-feature/style.scss +75 -0
  218. package/src/card-form/README.md +113 -0
  219. package/src/card-form/index.tsx +128 -0
  220. package/src/card-form/style.scss +20 -0
  221. package/src/card-settings-group/index.tsx +28 -3
  222. package/src/card-sortable-list/index.tsx +27 -4
  223. package/src/color-picker/index.js +15 -8
  224. package/src/color-picker/index.test.js +52 -0
  225. package/src/color-picker/style.scss +11 -13
  226. package/src/confirm-dialog/README.md +128 -0
  227. package/src/confirm-dialog/index.tsx +74 -6
  228. package/src/dataviews/index.tsx +34 -0
  229. package/src/dataviews/style.scss +9 -0
  230. package/src/grid/style.scss +6 -1
  231. package/src/handoff/README.md +93 -0
  232. package/src/handoff/index.js +46 -8
  233. package/src/hooks/index.ts +2 -1
  234. package/src/hooks/use-confirm-dialog.tsx +80 -0
  235. package/src/hooks/use-unsaved-changes-dialog.tsx +140 -0
  236. package/src/image-upload/index.js +10 -15
  237. package/src/image-upload/style.scss +12 -40
  238. package/src/index.js +7 -1
  239. package/src/integration-icons/active-campaign.js +14 -0
  240. package/src/integration-icons/constant-contact.js +22 -0
  241. package/src/integration-icons/fundraise-up.js +18 -0
  242. package/src/integration-icons/index.js +6 -0
  243. package/src/integration-icons/mailchimp.js +10 -0
  244. package/src/integration-icons/salesforce.js +10 -0
  245. package/src/integration-icons/wisepops.js +14 -0
  246. package/src/modal/style.scss +4 -0
  247. package/src/plugin-installer/index.js +30 -19
  248. package/src/section-header/index.js +56 -24
  249. package/src/section-header/style.scss +31 -11
  250. package/src/select-control/index.js +4 -0
  251. package/src/utils/editor-toolbar-back-button.tsx +48 -0
  252. package/src/utils/index.js +6 -0
  253. package/src/utils/style.scss +8 -0
  254. package/src/with-wizard/style.scss +4 -54
  255. package/src/with-wizard-screen/style.scss +41 -3
  256. package/src/wizard/index.js +90 -53
  257. package/src/wizard/store/index.js +2 -2
  258. package/dist/cjs/button-card/index.js +0 -72
  259. package/dist/cjs/button-card/style.scss +0 -180
  260. package/dist/esm/button-card/index.js +0 -64
  261. package/dist/esm/button-card/style.scss +0 -180
  262. package/src/button-card/index.js +0 -52
  263. package/src/button-card/style.scss +0 -180
@@ -0,0 +1,80 @@
1
+ /**
2
+ * WordPress dependencies.
3
+ */
4
+ import { useCallback, useRef, useState } from '@wordpress/element';
5
+
6
+ /**
7
+ * Internal dependencies.
8
+ */
9
+ import ConfirmDialog from '../confirm-dialog';
10
+
11
+ type UseConfirmDialogOptions = {
12
+ when?: boolean;
13
+ message: React.ReactNode;
14
+ title?: string;
15
+ confirmButtonText?: string;
16
+ cancelButtonText?: string;
17
+ isDestructive?: boolean;
18
+ hideTitle?: boolean;
19
+ size?: 'small' | 'medium' | 'large' | 'x-large' | 'full';
20
+ className?: string;
21
+ };
22
+
23
+ type UseConfirmDialogResult = {
24
+ confirmDialog: React.ReactElement;
25
+ requestConfirm: ( callback: () => void ) => void;
26
+ };
27
+
28
+ /**
29
+ * A hook that encapsulates the ConfirmDialog component and provides a
30
+ * `requestConfirm` function for imperative use.
31
+ *
32
+ * Calling `requestConfirm( callback )` will show a confirmation dialog.
33
+ * If the user confirms, `callback` is invoked. If the user cancels, it is not.
34
+ *
35
+ * When `when` is explicitly `false`, `requestConfirm( callback )` calls
36
+ * `callback` immediately without showing the dialog. This is useful for
37
+ * guarding actions that are only destructive when there are unsaved changes
38
+ * (e.g. `when={ isDirty }`). When `when` is omitted or `true`, the dialog
39
+ * is always shown.
40
+ *
41
+ * The `confirmDialog` element must be rendered somewhere in the component's
42
+ * JSX for the dialog to appear.
43
+ */
44
+ function useConfirmDialog( options: UseConfirmDialogOptions ): UseConfirmDialogResult {
45
+ const { message, when, ...dialogProps } = options;
46
+ const [ pendingAction, setPendingAction ] = useState< ( () => void ) | null >( null );
47
+
48
+ // Keep a ref so `requestConfirm` can always read the latest `when` value
49
+ // without needing to be recreated on every render.
50
+ const whenRef = useRef( when );
51
+ whenRef.current = when;
52
+
53
+ const requestConfirm = useCallback( ( callback: () => void ) => {
54
+ if ( whenRef.current !== false ) {
55
+ // Store as a thunk to opt out of React's functional-update interpretation.
56
+ setPendingAction( () => callback );
57
+ } else {
58
+ callback();
59
+ }
60
+ }, [] );
61
+
62
+ const confirmDialog = (
63
+ <ConfirmDialog
64
+ { ...dialogProps }
65
+ when={ when }
66
+ isOpen={ !! pendingAction }
67
+ onConfirm={ () => {
68
+ pendingAction?.();
69
+ setPendingAction( null );
70
+ } }
71
+ onCancel={ () => setPendingAction( null ) }
72
+ >
73
+ { message }
74
+ </ConfirmDialog>
75
+ );
76
+
77
+ return { confirmDialog, requestConfirm };
78
+ }
79
+
80
+ export default useConfirmDialog;
@@ -0,0 +1,140 @@
1
+ /**
2
+ * WordPress dependencies.
3
+ */
4
+ import { useEffect, useRef } from '@wordpress/element';
5
+ import { __ } from '@wordpress/i18n';
6
+
7
+ /**
8
+ * Internal dependencies.
9
+ */
10
+ import useConfirmDialog from './use-confirm-dialog';
11
+
12
+ type UseUnsavedChangesDialogOptions = {
13
+ when: boolean;
14
+ };
15
+
16
+ // Stack of mounted guards; the last entry owns the document-level handler so
17
+ // only the most-recently-mounted guard prompts when several are active. Using a
18
+ // stack keeps ownership correct under out-of-order unmounts.
19
+ const activeGuards: symbol[] = [];
20
+
21
+ /**
22
+ * Returns true when `href` resolves to the same origin as the current page —
23
+ * i.e. it is an internal navigation that would unload the wizard. External
24
+ * `https://...` links, `mailto:`, `tel:`, and other schemes navigate to a new
25
+ * context (new tab, mail client, dialer) without unloading the wizard, and
26
+ * must not trigger the discard-changes prompt.
27
+ */
28
+ function isSameOriginNavigation( link: HTMLAnchorElement ): boolean {
29
+ try {
30
+ const url = new URL( link.href, window.location.href );
31
+ return url.origin === window.location.origin && ( url.protocol === 'http:' || url.protocol === 'https:' );
32
+ } catch ( e ) {
33
+ return false;
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Shared unsaved-changes guard. Wraps `useConfirmDialog` with standardized
39
+ * messaging, intercepts same-origin link clicks so the dialog fires instead
40
+ * of a silent navigation, and adds a `beforeunload` listener as the last-resort
41
+ * guard for refresh / tab-close (browser-native, cannot be styled). The
42
+ * returned `confirmDialog` element must be rendered in JSX.
43
+ *
44
+ * Single-consumer constraint: the click handler is attached at the document
45
+ * level in capture phase. Two simultaneously-active instances will both fire
46
+ * a dialog. A development-only warning surfaces this.
47
+ */
48
+ function useUnsavedChangesDialog( { when }: UseUnsavedChangesDialogOptions ) {
49
+ const { confirmDialog, requestConfirm } = useConfirmDialog( {
50
+ when,
51
+ message: __( 'You have unsaved changes that will be lost. Discard changes?', 'newspack-plugin' ),
52
+ confirmButtonText: __( 'Discard changes', 'newspack-plugin' ),
53
+ hideTitle: true,
54
+ } );
55
+
56
+ // Tracks navigation the user has already approved via our custom dialog so
57
+ // the beforeunload guard doesn't fire a second native prompt on top of it.
58
+ const isNavigatingRef = useRef( false );
59
+
60
+ useEffect( () => {
61
+ if ( ! when ) {
62
+ return;
63
+ }
64
+ const ownerId = Symbol( 'unsaved-changes-guard' );
65
+ activeGuards.push( ownerId );
66
+ if ( process.env.NODE_ENV !== 'production' && activeGuards.length > 1 ) {
67
+ // eslint-disable-next-line no-console
68
+ console.warn(
69
+ 'useUnsavedChangesDialog: more than one active instance detected. ' +
70
+ 'Document-level click capture will fire a dialog per instance — ensure only one guard is active at a time.'
71
+ );
72
+ }
73
+ const handler = ( e: MouseEvent ) => {
74
+ // Only the top-of-stack guard prompts, so concurrent guards can't stack dialogs.
75
+ if ( activeGuards[ activeGuards.length - 1 ] !== ownerId ) {
76
+ return;
77
+ }
78
+ if ( e.defaultPrevented || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0 ) {
79
+ return;
80
+ }
81
+ const target = e.target as HTMLElement | null;
82
+ const link = target?.closest( 'a[href]' ) as HTMLAnchorElement | null;
83
+ if ( ! link ) {
84
+ return;
85
+ }
86
+ const href = link.getAttribute( 'href' );
87
+ if ( ! href || href.startsWith( '#' ) || href.startsWith( 'javascript:' ) ) {
88
+ // All `#`-prefixed links are skipped here: plain anchors
89
+ // (`#section`) are scroll targets, and HashRouter paths
90
+ // (`#/route`) are intercepted by `ConfirmDialog`'s built-in
91
+ // `history.block` listener instead. Routing them through
92
+ // `window.location.href` here would trigger a hashchange
93
+ // that the still-active block re-catches, double-prompting.
94
+ return;
95
+ }
96
+ if ( link.target && link.target !== '_self' ) {
97
+ return;
98
+ }
99
+ // Skip mailto:, tel:, external origins, and any non-http(s) scheme —
100
+ // they don't unload the wizard, so a discard prompt would be wrong.
101
+ if ( ! isSameOriginNavigation( link ) ) {
102
+ return;
103
+ }
104
+ e.preventDefault();
105
+ e.stopPropagation();
106
+ const destination = link.href;
107
+ requestConfirm( () => {
108
+ isNavigatingRef.current = true;
109
+ window.location.href = destination;
110
+ } );
111
+ };
112
+ document.addEventListener( 'click', handler, true );
113
+ return () => {
114
+ document.removeEventListener( 'click', handler, true );
115
+ const idx = activeGuards.indexOf( ownerId );
116
+ if ( idx !== -1 ) {
117
+ activeGuards.splice( idx, 1 );
118
+ }
119
+ };
120
+ }, [ when, requestConfirm ] );
121
+
122
+ useEffect( () => {
123
+ if ( ! when ) {
124
+ return;
125
+ }
126
+ const handler = ( e: BeforeUnloadEvent ) => {
127
+ if ( isNavigatingRef.current ) {
128
+ return;
129
+ }
130
+ e.preventDefault();
131
+ e.returnValue = '';
132
+ };
133
+ window.addEventListener( 'beforeunload', handler );
134
+ return () => window.removeEventListener( 'beforeunload', handler );
135
+ }, [ when ] );
136
+
137
+ return { confirmDialog, requestConfirm };
138
+ }
139
+
140
+ export default useUnsavedChangesDialog;
@@ -7,11 +7,12 @@
7
7
  */
8
8
  import { Component } from '@wordpress/element';
9
9
  import { __ } from '@wordpress/i18n';
10
+ import { BaseControl } from '@wordpress/components';
10
11
 
11
12
  /**
12
13
  * Internal dependencies.
13
14
  */
14
- import { Button, InfoButton } from '../';
15
+ import { Button } from '../';
15
16
  import './style.scss';
16
17
 
17
18
  /**
@@ -72,41 +73,35 @@ class ImageUpload extends Component {
72
73
  * Render.
73
74
  */
74
75
  render = () => {
75
- const { buttonLabel, className, disabled, help, image, info, isCovering, label, onChange, style = {} } = this.props;
76
+ const { buttonLabel, className, disabled, help, image, isCovering, label, onChange, style = {} } = this.props;
76
77
  const classes = classnames(
77
78
  'newspack-image-upload__image',
78
79
  { 'newspack-image-upload__image--has-image': image },
79
80
  { 'newspack-image-upload__image--covering': isCovering }
80
81
  );
81
82
  return (
82
- <div className={ classnames( 'newspack-image-upload', className ) }>
83
- <div className="newspack-image-upload__header">
84
- { /* eslint-disable-next-line jsx-a11y/label-has-for, jsx-a11y/label-has-associated-control */ }
85
- { label && <label className="newspack-image-upload__label">{ label }</label> }
86
- { info && <InfoButton text={ info } /> }
87
- </div>
88
- <div className={ classes } style={ { ...style } }>
83
+ <BaseControl __nextHasNoMarginBottom className={ classnames( 'newspack-image-upload', className ) } help={ help }>
84
+ { label && <BaseControl.VisualLabel>{ label }</BaseControl.VisualLabel> }
85
+ <div className={ classes } style={ style }>
89
86
  { image?.url ? (
90
87
  <>
91
88
  <img data-testid="image-upload" src={ image.url } alt={ __( 'Image preview', 'newspack-plugin' ) } />
92
89
  <div className="newspack-image-upload__controls">
93
- <Button disabled={ disabled } onClick={ this.openModal } isLink>
90
+ <Button disabled={ disabled } onClick={ this.openModal } variant="tertiary">
94
91
  { __( 'Replace', 'newspack-plugin' ) }
95
92
  </Button>
96
- <span className="sep" />
97
- <Button disabled={ disabled } onClick={ () => onChange( null ) } isLink isDestructive>
93
+ <Button disabled={ disabled } onClick={ () => onChange( null ) } variant="tertiary" isDestructive>
98
94
  { __( 'Remove', 'newspack-plugin' ) }
99
95
  </Button>
100
96
  </div>
101
97
  </>
102
98
  ) : (
103
- <Button disabled={ disabled } onClick={ this.openModal } isLink>
99
+ <Button disabled={ disabled } onClick={ this.openModal } variant="tertiary">
104
100
  { buttonLabel ? buttonLabel : __( 'Upload', 'newspack-plugin' ) }
105
101
  </Button>
106
102
  ) }
107
103
  </div>
108
- { help && <p className="newspack-image-upload__help">{ help }</p> }
109
- </div>
104
+ </BaseControl>
110
105
  );
111
106
  };
112
107
  }
@@ -3,45 +3,29 @@
3
3
  */
4
4
 
5
5
  @use "~@wordpress/base-styles/colors" as wp-colors;
6
+ @use "~@wordpress/base-styles/variables" as wp-vars;
6
7
 
7
8
  .newspack-image-upload {
8
- margin: 32px 0;
9
- &__header {
10
- display: flex;
11
- }
12
- &__label {
13
- margin: 0 0 8px;
14
- }
9
+ margin: wp-vars.$grid-unit-40 0;
15
10
  &__image {
16
11
  align-items: center;
12
+ background-position: center;
17
13
  background-repeat: no-repeat;
18
14
  background-size: contain;
19
- background-position: center;
20
- border: 1px dashed wp-colors.$gray-700;
21
- border-radius: 2px;
15
+ border: 1px dashed wp-colors.$gray-600;
16
+ border-radius: wp-vars.$radius-small;
22
17
  display: flex;
23
18
  justify-content: center;
24
19
  max-width: 100%;
25
- min-height: 48px;
20
+ min-height: wp-vars.$grid-unit-50;
26
21
  position: relative;
27
22
  &--covering {
28
23
  background-size: cover;
29
24
  }
30
25
  &:not(&--has-image) {
31
- .newspack-button {
32
- background: white !important;
33
- border-radius: 2px !important;
34
- padding: 4px 16px !important;
35
-
36
- &:active,
37
- &:focus,
38
- &:hover {
39
- background: white !important;
40
- }
41
-
42
- &:focus {
43
- outline-offset: -3px !important;
44
- }
26
+ .components-button {
27
+ justify-content: center;
28
+ width: calc(100% - 2px);
45
29
  }
46
30
  }
47
31
  &--has-image {
@@ -49,8 +33,8 @@
49
33
  overflow: hidden;
50
34
  &::before,
51
35
  &::after {
52
- border: 1px solid rgba(black, 0.54);
53
- border-radius: 2px;
36
+ border: 1px solid rgba(wp-colors.$black, 0.54);
37
+ border-radius: wp-vars.$radius-small;
54
38
  content: "";
55
39
  inset: 0;
56
40
  position: absolute;
@@ -66,7 +50,7 @@
66
50
  position: relative;
67
51
  }
68
52
  &::after {
69
- background: rgba(white, 0.9);
53
+ background: rgba(wp-colors.$white, 0.95);
70
54
  border-color: wp-colors.$gray-700;
71
55
  }
72
56
  &:focus-within,
@@ -80,20 +64,8 @@
80
64
  max-height: 100%;
81
65
  max-width: 100%;
82
66
  }
83
- .sep {
84
- background: transparent;
85
- display: block;
86
- height: 8px;
87
- margin: 0;
88
- width: 100%;
89
- }
90
67
  }
91
68
  }
92
- &__image + &__help {
93
- color: wp-colors.$gray-700;
94
- font-size: 12px;
95
- margin: 1em 0 0;
96
- }
97
69
  &__controls {
98
70
  align-items: center;
99
71
  display: flex;
package/src/index.js CHANGED
@@ -5,15 +5,17 @@ export { default as AutocompleteWithSuggestions } from './autocomplete-with-sugg
5
5
  export { default as AutocompleteWithLatestPosts } from './autocomplete-with-latest-posts';
6
6
  export { default as Badge } from './badge';
7
7
  export { default as Button } from './button';
8
- export { default as ButtonCard } from './button-card';
9
8
  export { default as BoxContrast } from './box-contrast';
10
9
  export { default as Card } from './card';
10
+ export { default as CardFeature } from './card-feature';
11
+ export { default as CardForm } from './card-form';
11
12
  export { default as CardSettingsGroup } from './card-settings-group';
12
13
  export { default as CardSortableList } from './card-sortable-list';
13
14
  export { default as CategoryAutocomplete } from './category-autocomplete';
14
15
  export { default as ColorPicker } from './color-picker';
15
16
  export { default as ConfirmDialog } from './confirm-dialog';
16
17
  export { default as CustomSelectControl } from './custom-select-control';
18
+ export { default as DataViews } from './dataviews';
17
19
  export { default as Divider } from './divider';
18
20
  export { default as FormTokenField } from './form-token-field';
19
21
  export { default as Footer } from './footer';
@@ -48,6 +50,10 @@ export { default as withWizardScreen } from './with-wizard-screen';
48
50
 
49
51
  export { default as Router } from './proxied-imports/router';
50
52
  export { default as hooks } from './hooks';
53
+ export { default as useConfirmDialog } from './hooks/use-confirm-dialog';
54
+ export { default as useUnsavedChangesDialog } from './hooks/use-unsaved-changes-dialog';
55
+ import * as integrationIcons from './integration-icons';
56
+ export { integrationIcons };
51
57
  export { default as utils } from './utils';
52
58
 
53
59
  import './style.scss';
@@ -0,0 +1,14 @@
1
+ const activeCampaign = (
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
3
+ <path
4
+ d="M16.6934 10.5811C17.1772 10.9036 17.4355 11.4194 17.4355 11.9678C17.4355 12.516 17.1449 13.0319 16.6934 13.3545L6.56445 20V18.6133C6.56445 18.1939 6.75844 17.7746 7.14551 17.5166L15.5 11.9678L7.14551 6.38672C6.79063 6.16086 6.56445 5.74175 6.56445 5.29004V4L16.6934 10.5811Z"
5
+ fill="white"
6
+ />
7
+ <path
8
+ d="M6.56445 8.77441C6.56445 8.35519 7.04853 8.12873 7.37109 8.35449L12.6611 11.9355L11.9512 12.4189C11.4996 12.7091 10.9193 12.7092 10.4678 12.4189L9.30664 11.6777L6.56445 9.83887V8.77441Z"
9
+ fill="white"
10
+ />
11
+ </svg>
12
+ );
13
+
14
+ export default activeCampaign;
@@ -0,0 +1,22 @@
1
+ const constantContact = (
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
3
+ <path
4
+ d="M11.3261 20.9997C6.36799 20.9997 3 16.9581 3 12.6626C3 8.30073 6.43425 4.5352 11.0059 4.35852C11.1826 4.34748 11.3372 4.50207 11.3372 4.67875V6.01491C11.3372 6.18055 11.2047 6.3241 11.028 6.33514C7.67102 6.48974 4.96558 9.23935 4.96558 12.6736C4.96558 16.0305 7.61581 19.0341 11.3261 19.0341C14.9371 19.0341 17.4989 16.152 17.6535 12.9717C17.6646 12.8061 17.7971 12.6626 17.9738 12.6626H19.3099C19.4866 12.6626 19.6302 12.8061 19.6302 12.9938C19.4756 17.2342 16.0634 20.9997 11.3261 20.9997Z"
5
+ fill="#1856ED"
6
+ />
7
+ <path
8
+ d="M11.3372 16.5827C9.12864 16.5827 7.41704 14.8269 7.41704 12.6625C7.41704 10.6417 8.97405 8.95223 10.9948 8.78659C11.1826 8.77555 11.3372 8.9191 11.3372 9.10683V10.443C11.3372 10.5976 11.2267 10.7411 11.0611 10.7632C10.0894 10.8957 9.38262 11.7239 9.38262 12.6736C9.38262 13.7337 10.2108 14.6281 11.3372 14.6281C12.2758 14.6281 13.115 13.9214 13.2475 12.9497C13.2696 12.7951 13.4132 12.6736 13.5678 12.6736H14.9039C15.0916 12.6736 15.2352 12.8282 15.2242 13.0159C15.0475 14.9925 13.3911 16.5827 11.3372 16.5827Z"
9
+ fill="#1856ED"
10
+ />
11
+ <path
12
+ d="M19.0339 11.0283C18.8793 7.79277 16.2953 5.12046 12.9715 4.96586C12.8058 4.95482 12.6623 4.82231 12.6623 4.64563V3.32051C12.6623 3.14383 12.8058 3.00028 12.9936 3.00028C17.3112 3.16592 20.8338 6.62225 20.9994 11.0062C21.0105 11.1828 20.8559 11.3374 20.6792 11.3374H19.3431C19.1885 11.3374 19.0449 11.2049 19.0339 11.0283Z"
13
+ fill="#FF9E1A"
14
+ />
15
+ <path
16
+ d="M13.0267 9.41602C12.8279 9.38289 12.6844 9.21725 12.6844 9.05161V7.74859C12.6844 7.56086 12.839 7.41731 13.0267 7.42835C14.9481 7.59399 16.4278 9.12891 16.5934 10.9841C16.6045 11.1718 16.4609 11.3264 16.2732 11.3264H14.8708C14.7383 11.3264 14.6389 11.238 14.6168 11.1055C14.5174 10.3215 13.9211 9.57062 13.0267 9.41602Z"
17
+ fill="#FF9E1A"
18
+ />
19
+ </svg>
20
+ );
21
+
22
+ export default constantContact;
@@ -0,0 +1,18 @@
1
+ const fundraiseUp = (
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
3
+ <path
4
+ d="M4.90143 6.11914C4.90143 4.94655 5.85727 4 7.03802 4H19.0985V5.75182C19.0985 6.92441 18.1427 7.87098 16.9619 7.87098H4.90143V6.11914Z"
5
+ fill="white"
6
+ />
7
+ <path
8
+ d="M4.90143 11.4721C4.90143 10.6979 5.53463 10.0645 6.33667 10.0645H14.8778V11.824C14.8778 12.9924 13.921 13.9355 12.739 13.9355H4.90143V11.4721Z"
9
+ fill="white"
10
+ />
11
+ <path
12
+ d="M4.90143 16.8329C4.90143 16.4387 5.22775 16.1291 5.62502 16.1291L10.657 16.129V17.8886C10.657 19.0569 9.69221 20 8.50045 20H4.90143V16.8329Z"
13
+ fill="white"
14
+ />
15
+ </svg>
16
+ );
17
+
18
+ export default fundraiseUp;
@@ -0,0 +1,6 @@
1
+ export { default as activeCampaign } from './active-campaign';
2
+ export { default as constantContact } from './constant-contact';
3
+ export { default as fundraiseUp } from './fundraise-up';
4
+ export { default as mailchimp } from './mailchimp';
5
+ export { default as salesforce } from './salesforce';
6
+ export { default as wisepops } from './wisepops';
@@ -0,0 +1,10 @@
1
+ const mailchimp = (
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
3
+ <path
4
+ d="M21.5694 14.9382L21.214 13.9313C21.214 13.9313 22.6947 11.6807 19.0226 10.9108C19.0226 10.9108 19.2595 8.12707 17.9565 6.82408C20.7995 4.04046 20.148 -0.16465 13.6922 2.55975C10.3755 -3.77748 -2.41746 11.0884 2.91293 13.4575C2.37994 14.1683 2.37994 17.7218 6.05198 18.0772C8.53946 23.4076 14.5806 23.763 18.075 22.1638C21.5694 20.5647 23.5831 15.4712 21.5694 14.9382ZM5.99277 17.3072C2.97214 17.0111 2.67608 12.8653 5.28199 12.4506C7.88797 12.036 8.53946 17.5442 5.99277 17.3072ZM5.10435 11.6807C4.27514 11.6807 3.26829 12.806 3.26829 12.806C-0.759109 10.8516 10.5532 -2.11914 12.9815 2.91511C12.9815 2.91511 7.05883 5.75802 5.10435 11.6807ZM16.9497 16.7149C16.9497 16.4781 15.7059 17.0703 13.4553 16.3004C13.633 15.0566 16.2982 17.3664 20.7402 14.3459L21.0956 15.5897C22.7539 15.2935 21.0956 20.9201 15.7651 20.8608C11.4416 20.8016 10.0794 16.3596 12.4484 13.9313C12.9223 13.4575 10.7309 12.5099 11.1454 10.4369C11.3232 9.54857 12.0931 8.24557 14.0476 8.60093C16.0021 8.95628 16.4166 7.17944 17.7196 7.83093C19.0226 8.48243 18.2527 10.97 18.4303 11.3253C18.608 11.6807 20.5033 11.7399 20.8587 12.7468C21.214 13.7536 18.4303 15.945 14.1068 15.3527C13.0999 15.2343 12.5077 16.5373 13.1591 17.3664C14.4621 19.2617 19.7926 18.0179 20.681 16.1819C18.4303 17.8995 13.8106 18.551 13.4553 16.7149C14.7583 17.3072 16.9497 16.9519 16.9497 16.7149ZM9.19096 7.35715C10.494 5.75802 12.2116 4.81038 12.2116 4.81038L11.8562 5.69881C11.8562 5.69881 13.0999 4.75117 14.4621 4.75117L13.9884 5.22495C15.5283 5.28424 16.1798 5.87644 16.1798 5.87644C16.1798 5.87644 12.5669 4.81038 9.19096 7.35715ZM17.1866 9.66699C17.9565 9.60778 17.7196 11.3846 17.7196 11.3846H17.2458C17.2458 11.3846 16.4166 9.72621 17.1866 9.66699ZM13.6922 11.6215C13.1591 11.6807 12.5669 11.9768 12.6262 11.7399C12.8631 10.7923 15.0544 11.0292 14.9952 11.8584C14.936 12.6875 14.4621 11.5031 13.6922 11.6215ZM14.936 12.3322C14.9952 12.4506 14.5214 12.3322 14.166 12.3914C13.8106 12.4506 13.4553 12.6283 13.4553 12.5099C13.4553 12.3914 14.8175 11.8584 14.936 12.3322ZM16.1205 12.5099C16.2982 12.1545 17.0089 12.5099 16.8313 12.8653C16.6536 13.2206 15.9428 12.8653 16.1205 12.5099ZM17.6012 12.6283C17.2458 12.6283 17.2458 11.8584 17.6012 11.8584C17.9565 11.8584 17.9565 12.6875 17.6012 12.6875V12.6283ZM6.94033 15.7674C7.11805 15.945 6.58498 16.3004 6.17041 16.0042C5.75584 15.7081 6.64427 14.2867 5.57813 13.9313C4.51206 13.576 4.8082 14.9382 4.51206 14.7605C4.21592 14.5828 4.92663 12.6875 6.17041 13.4575C7.41419 14.2275 5.81505 15.412 6.52576 15.7674C7.23647 16.1227 6.82191 15.6489 6.94033 15.7674Z"
5
+ fill="#241C15"
6
+ />
7
+ </svg>
8
+ );
9
+
10
+ export default mailchimp;
@@ -0,0 +1,10 @@
1
+ const salesforce = (
2
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
3
+ <path
4
+ d="M13.3173 7.24314C14.3498 6.16866 15.7848 5.50017 17.3737 5.50017C19.4877 5.50017 21.3287 6.67965 22.3122 8.42612C23.1661 8.04463 24.1111 7.83113 25.1016 7.83113C28.913 7.83113 32 10.9461 32 14.7925C32 18.639 28.9095 21.7539 25.1016 21.7539C24.6361 21.7539 24.1811 21.7084 23.7436 21.6174C22.8791 23.1574 21.2307 24.2004 19.3407 24.2004C18.5497 24.2004 17.8007 24.0184 17.1357 23.6929C16.2607 25.7543 14.2168 27.1998 11.8403 27.1998C9.46385 27.1998 7.25189 25.6319 6.4399 23.4339C6.0864 23.5074 5.71891 23.5494 5.34092 23.5494C2.39046 23.5494 0 21.1344 0 18.1525C0 16.154 1.07448 14.411 2.67046 13.4765C2.34146 12.7206 2.15947 11.8841 2.15947 11.0091C2.15947 7.57913 4.94192 4.80018 8.37537 4.80018C10.3913 4.80018 12.1833 5.75916 13.3173 7.24314Z"
5
+ fill="var(--integration-icon-color, #0D9DDA)"
6
+ />
7
+ </svg>
8
+ );
9
+
10
+ export default salesforce;
@@ -0,0 +1,14 @@
1
+ const wisepops = (
2
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
3
+ <path
4
+ d="M19.0543 10.8641C19.5935 10.8642 19.9806 11.384 19.8262 11.9008L16.1245 24.2947C16.0225 24.6356 15.7087 24.8698 15.3526 24.8698H10.5242C10.5199 24.8699 10.5155 24.8702 10.5112 24.8702H3.28999C2.96651 24.8702 2.67422 24.6766 2.548 24.3789L0.064734 18.5208C-0.160398 17.9896 0.229352 17.4005 0.80633 17.4004H8.02794C8.35135 17.4004 8.64327 17.5941 8.76954 17.8918L8.99143 18.4154L11.0754 11.4393C11.1773 11.0982 11.4911 10.8641 11.8473 10.8641H19.0543Z"
5
+ fill="var(--integration-icon-color, #010101)"
6
+ />
7
+ <path
8
+ d="M31.1937 7.12976C31.7311 7.12979 32.1179 7.646 31.9671 8.1617L27.2512 24.2907C27.1508 24.6344 26.8355 24.8702 26.4778 24.8702H19.3384C18.8011 24.8702 18.4142 24.3544 18.5649 23.8387L23.2812 7.70967C23.3816 7.36609 23.6965 7.12981 24.0543 7.12976H31.1937Z"
9
+ fill="var(--integration-icon-color, #010101)"
10
+ />
11
+ </svg>
12
+ );
13
+
14
+ export default wisepops;
@@ -95,3 +95,7 @@
95
95
  }
96
96
  }
97
97
  }
98
+ /** Hide dropdown menus when a modal is open */
99
+ .modal-open .components-popover__content {
100
+ opacity: 0;
101
+ }
@@ -96,12 +96,11 @@ class PluginInstaller extends Component {
96
96
  };
97
97
  return apiFetch( params )
98
98
  .then( response => {
99
- const { pluginInfo } = this.state;
100
99
  this.props.onInstalled( slug );
101
- this.updatePluginInfo( {
102
- ...pluginInfo,
100
+ return this.updatePluginInfo( prev => ( {
101
+ ...prev,
103
102
  [ slug ]: { ...response, installationStatus: PLUGIN_STATE_ACTIVE },
104
- } );
103
+ } ) );
105
104
  } )
106
105
  .catch( error => {
107
106
  this.setInstallationStatus( slug, PLUGIN_STATE_ERROR, error.message );
@@ -109,28 +108,40 @@ class PluginInstaller extends Component {
109
108
  };
110
109
 
111
110
  setChecked = ( slug, checked ) => {
112
- const { pluginInfo } = this.state;
113
- this.updatePluginInfo( { ...pluginInfo, [ slug ]: { ...pluginInfo[ slug ], checked } } );
111
+ this.updatePluginInfo( prev => ( {
112
+ ...prev,
113
+ [ slug ]: { ...prev[ slug ], checked },
114
+ } ) );
114
115
  };
115
116
 
116
117
  setInstallationStatus = ( slug, installationStatus, notification = null ) => {
117
- const { pluginInfo } = this.state;
118
- this.updatePluginInfo( {
119
- ...pluginInfo,
120
- [ slug ]: { ...pluginInfo[ slug ], installationStatus, notification },
121
- } );
118
+ this.updatePluginInfo( prev => ( {
119
+ ...prev,
120
+ [ slug ]: { ...prev[ slug ], installationStatus, notification },
121
+ } ) );
122
122
  };
123
123
 
124
- updatePluginInfo = pluginInfo => {
124
+ // Accepts either the next pluginInfo object or an updater `prev => next`.
125
+ // The updater form is required for serial install chains: setState batches
126
+ // pending updates inside the same microtask, so reading `this.state.pluginInfo`
127
+ // at call time can return a snapshot that predates an earlier-in-the-chain
128
+ // pending write, and the second update would clobber the first.
129
+ updatePluginInfo = pluginInfoOrUpdater => {
125
130
  return new Promise( resolve => {
126
131
  const { onStatus } = this.props;
127
- this.setState( { pluginInfo }, () => {
128
- const complete = Object.values( pluginInfo ).every( plugin => {
129
- return 'active' === plugin.Status;
130
- } );
131
- onStatus( { complete, pluginInfo } );
132
- resolve();
133
- } );
132
+ this.setState(
133
+ prevState => ( {
134
+ pluginInfo: typeof pluginInfoOrUpdater === 'function' ? pluginInfoOrUpdater( prevState.pluginInfo ) : pluginInfoOrUpdater,
135
+ } ),
136
+ () => {
137
+ const { pluginInfo } = this.state;
138
+ const complete = Object.values( pluginInfo ).every( plugin => {
139
+ return 'active' === plugin.Status;
140
+ } );
141
+ onStatus( { complete, pluginInfo } );
142
+ resolve();
143
+ }
144
+ );
134
145
  } );
135
146
  };
136
147