@automattic/newspack-blocks 1.37.0 → 1.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. package/.cache/babel/00255e6096be9e31ec354db849dfefaa.json.gz +0 -0
  2. package/.cache/babel/0265bee20d709ac92f56456637727f4b.json.gz +0 -0
  3. package/.cache/babel/02986ba19dc461a152ea1c45f3f36a27.json.gz +0 -0
  4. package/.cache/babel/06a1700c1a4a989fc7886c86bf6223f7.json.gz +0 -0
  5. package/.cache/babel/07169c73863508b392f15022920bd0b6.json.gz +0 -0
  6. package/.cache/babel/0a5083e561829e3884cb5c71388adc3f.json.gz +0 -0
  7. package/.cache/babel/0a713997b6e4f45849ed6a4bff79cca5.json.gz +0 -0
  8. package/.cache/babel/0a9506bf649ffbe854e75459cf6b8972.json.gz +0 -0
  9. package/.cache/babel/0c12c4398bbbf2d771b13dd1d0e64097.json.gz +0 -0
  10. package/.cache/babel/0f8c18da204f4cc2777ca3191f10c309.json.gz +0 -0
  11. package/.cache/babel/0fced15ed787cc4239bb53c1d12892e6.json.gz +0 -0
  12. package/.cache/babel/10faf7c74a89e9aa4d8b9528a654913e.json.gz +0 -0
  13. package/.cache/babel/1353b411b65a123b5dc35012db11aa1d.json.gz +0 -0
  14. package/.cache/babel/1729336f326db1cf54b9c1671dc5e36c.json.gz +0 -0
  15. package/.cache/babel/17a433ab631a32d0712f3f75cf85ea99.json.gz +0 -0
  16. package/.cache/babel/1948e72d907f681ff771881ad6800ee1.json.gz +0 -0
  17. package/.cache/babel/1ab4bf44aa14d2eb7e6911900a3b2c12.json.gz +0 -0
  18. package/.cache/babel/1decfd137cac87a75bd83ffbd39c9106.json.gz +0 -0
  19. package/.cache/babel/2143ceff6d186bc13bc902e0f1f33dbf.json.gz +0 -0
  20. package/.cache/babel/23c63332718e167968a7c2a8a5f730eb.json.gz +0 -0
  21. package/.cache/babel/24bf53676e12293cbf88d01e649fd167.json.gz +0 -0
  22. package/.cache/babel/26ba0bb46844575022e54a8819fbf405.json.gz +0 -0
  23. package/.cache/babel/281ca171ce8947f87f574df11059e00b.json.gz +0 -0
  24. package/.cache/babel/28700b83434e4dbfb9a20d7ec0a7e7a4.json.gz +0 -0
  25. package/.cache/babel/29cf8eeea8630ade027e34459f2e49ee.json.gz +0 -0
  26. package/.cache/babel/2b0dd254a46223a93e6259d2b797a7e7.json.gz +0 -0
  27. package/.cache/babel/2da6786ede3ddda4c86eeac524a6a796.json.gz +0 -0
  28. package/.cache/babel/2e7ce10fdee94274b48d528f94c32b34.json.gz +0 -0
  29. package/.cache/babel/2f93fed1706a40d82122281cc3677074.json.gz +0 -0
  30. package/.cache/babel/3a59b710e9745ef93148d6d74ee7d16f.json.gz +0 -0
  31. package/.cache/babel/3b2c440027c793c8f7978d7622cbfca0.json.gz +0 -0
  32. package/.cache/babel/3ca939ace4af88e266942d93837ab37a.json.gz +0 -0
  33. package/.cache/babel/3e671670c4d395cd1804ef4be31955e4.json.gz +0 -0
  34. package/.cache/babel/3e69fe002d9973c0030152a51da9477a.json.gz +0 -0
  35. package/.cache/babel/3f5f8ea603fd57850c6683c750fe53d8.json.gz +0 -0
  36. package/.cache/babel/41abaaadb68b0657a95fa0c356873def.json.gz +0 -0
  37. package/.cache/babel/421ac952d6b472ba18e1cb95ef65f42a.json.gz +0 -0
  38. package/.cache/babel/424185dd50e91d8dee5fdae43086010e.json.gz +0 -0
  39. package/.cache/babel/427a6423ebde76bcb488747127e47351.json.gz +0 -0
  40. package/.cache/babel/43e800659f23430a181fb4fc328eb6ef.json.gz +0 -0
  41. package/.cache/babel/469651017a2b6f3b9f63fda034974b3a.json.gz +0 -0
  42. package/.cache/babel/480d26447b7ee019a37e4e18422403e9.json.gz +0 -0
  43. package/.cache/babel/4b58fad313d90a4c1577a2766e6c924c.json.gz +0 -0
  44. package/.cache/babel/4d2d42fea63650192d1a3a5648b621c5.json.gz +0 -0
  45. package/.cache/babel/4d8c26a76e77fa64d01f30a5f2d62f85.json.gz +0 -0
  46. package/.cache/babel/4f606ce6656565507a74aaac8dc21eea.json.gz +0 -0
  47. package/.cache/babel/505f37592f4c601ccdc1581ca89d9009.json.gz +0 -0
  48. package/.cache/babel/50aaf34aa54b2cd3058518e42850bb62.json.gz +0 -0
  49. package/.cache/babel/51fbf389462feb111228ba7ec64c631d.json.gz +0 -0
  50. package/.cache/babel/51fc90ae5fe129a095172032f304dc2c.json.gz +0 -0
  51. package/.cache/babel/53dbf5484c5601528c2fd8d7fe728b78.json.gz +0 -0
  52. package/.cache/babel/58e4535574062eb2d8fa56d002e7190d.json.gz +0 -0
  53. package/.cache/babel/5aa5e8d1492938c62999de022c4b220e.json.gz +0 -0
  54. package/.cache/babel/5d64e475284e52c104df17e26066b17b.json.gz +0 -0
  55. package/.cache/babel/6333c664d69f13036f42bcf575c8e65d.json.gz +0 -0
  56. package/.cache/babel/635c439786b8522888342779b28ee9ee.json.gz +0 -0
  57. package/.cache/babel/648cffdd5ec4a2446f8935a17b98080b.json.gz +0 -0
  58. package/.cache/babel/64b0dd478e6f2dd51de5ee20e311aee7.json.gz +0 -0
  59. package/.cache/babel/6531721abbc0b3906b28b268ed7914af.json.gz +0 -0
  60. package/.cache/babel/66bf5585c7901a76c7d4014fa9ffed40.json.gz +0 -0
  61. package/.cache/babel/6ef313cb6d3a59f73a6b78e1a729fb13.json.gz +0 -0
  62. package/.cache/babel/6f14eed506421f15710d3dfafe3dcd93.json.gz +0 -0
  63. package/.cache/babel/70f6c01d8c6bc3efc41c97eaf49f7617.json.gz +0 -0
  64. package/.cache/babel/72321dde68f0bb9c334d90d6f4584be8.json.gz +0 -0
  65. package/.cache/babel/76c815fab58595f57e01af6591791d57.json.gz +0 -0
  66. package/.cache/babel/7c10f4d554daa8efe1e0a4457fb6d27b.json.gz +0 -0
  67. package/.cache/babel/7dffccddd9780ab37fb79f809947c2cd.json.gz +0 -0
  68. package/.cache/babel/7e5162e3319cd5d6fca6c0db0516aaeb.json.gz +0 -0
  69. package/.cache/babel/803192c922f7d419648bab7e1201b22e.json.gz +0 -0
  70. package/.cache/babel/81f3f0f896d29cdf88f8543dedb77787.json.gz +0 -0
  71. package/.cache/babel/84c019d3d643195fb37d08110f15f8c9.json.gz +0 -0
  72. package/.cache/babel/853c31db7c4351f2f36d1021c667224d.json.gz +0 -0
  73. package/.cache/babel/88ec99735ea4f314937736f04360ff8c.json.gz +0 -0
  74. package/.cache/babel/8959ab8fcfa783cb56e9418b99c0951c.json.gz +0 -0
  75. package/.cache/babel/8b9624d721f8706682e33fe1853c8287.json.gz +0 -0
  76. package/.cache/babel/8de5c373bbc4b6fbeec373d582935887.json.gz +0 -0
  77. package/.cache/babel/8f24517cff634d2e91c19e51be8816cc.json.gz +0 -0
  78. package/.cache/babel/901e1bb823eca196b594baa35a35a81c.json.gz +0 -0
  79. package/.cache/babel/90dbc666ff9fa3b88f305fe90a9917bf.json.gz +0 -0
  80. package/.cache/babel/91b62dfd9475df7a39dc08fbd43847f9.json.gz +0 -0
  81. package/.cache/babel/956daf27323e3b87bf8bf9bbf58881ed.json.gz +0 -0
  82. package/.cache/babel/959e9b81103c539a9d85ac60955451b9.json.gz +0 -0
  83. package/.cache/babel/963b1e44be998c4dcc6845745c377369.json.gz +0 -0
  84. package/.cache/babel/966b41ac1317b3e03ca1e59f7eb57e8c.json.gz +0 -0
  85. package/.cache/babel/969d5992cc682e3b43761ef02c10b851.json.gz +0 -0
  86. package/.cache/babel/99df128bb528d0f95a56b311801d14c1.json.gz +0 -0
  87. package/.cache/babel/9b33e7d21f3fc4955a536a033cced71b.json.gz +0 -0
  88. package/.cache/babel/9c5ec84f77314a621c01ad77da7ccb58.json.gz +0 -0
  89. package/.cache/babel/9d325af12a88623602a5f8c2fce04847.json.gz +0 -0
  90. package/.cache/babel/a413e4d325543836d0800684bb26ed04.json.gz +0 -0
  91. package/.cache/babel/a5405f86b8d1485e62a125275507e60f.json.gz +0 -0
  92. package/.cache/babel/a7f7461321ca6fd4360df04bb894c106.json.gz +0 -0
  93. package/.cache/babel/a8a6812e1c7b65cc6d0bdebe2a35c8ca.json.gz +0 -0
  94. package/.cache/babel/aa89cfc4c88fcc465b8d74af824e7f6b.json.gz +0 -0
  95. package/.cache/babel/adbaeb5ddf5612b3dcd233a3f6f17bf2.json.gz +0 -0
  96. package/.cache/babel/ae18cd6ec42a3366c73435b62a9f5f70.json.gz +0 -0
  97. package/.cache/babel/aea149ee1ec6fa3ca7839c63f5edd78d.json.gz +0 -0
  98. package/.cache/babel/af1b34067a69854e109c0b0e45d90e43.json.gz +0 -0
  99. package/.cache/babel/b3a335ab950f502914013810b43b0ad5.json.gz +0 -0
  100. package/.cache/babel/b6cfba15cda3404d4b1041df448ed2b2.json.gz +0 -0
  101. package/.cache/babel/ba711530bd319618a0510d8361bebb45.json.gz +0 -0
  102. package/.cache/babel/ba77e682225df6126b636458055601d9.json.gz +0 -0
  103. package/.cache/babel/bab8189a74eee8d72702d1510b52b3b3.json.gz +0 -0
  104. package/.cache/babel/bd8741020db56bb5d999252812d1d717.json.gz +0 -0
  105. package/.cache/babel/beaa874c5e01203d9dbe23cb89405276.json.gz +0 -0
  106. package/.cache/babel/c196c9b8c21953c474acdaa9029b3cbf.json.gz +0 -0
  107. package/.cache/babel/c1e31e6c217023eb3d3fd5d3de486ac3.json.gz +0 -0
  108. package/.cache/babel/c3aa996443e7f377aa423bcc03ffc617.json.gz +0 -0
  109. package/.cache/babel/c481eae84d69b65405a44af442dbfea7.json.gz +0 -0
  110. package/.cache/babel/c4a817d6c4bf6eb3687ffccc5c2df801.json.gz +0 -0
  111. package/.cache/babel/c8b10cf6f706fb669d6dd7564385768d.json.gz +0 -0
  112. package/.cache/babel/caa2f98ba4af6eba0605543f690693c6.json.gz +0 -0
  113. package/.cache/babel/cada4b2cecb2c1fcc48b7a7ecda54907.json.gz +0 -0
  114. package/.cache/babel/d1bed5404789c427c32965ac6ecad0d9.json.gz +0 -0
  115. package/.cache/babel/d395e8c0a9c63680f2dcf90ca0e91d4a.json.gz +0 -0
  116. package/.cache/babel/d4672090e0dd9a73bfb00bd770b23643.json.gz +0 -0
  117. package/.cache/babel/d4c81f65fe6d78cdbdcf1ef901b7ba1b.json.gz +0 -0
  118. package/.cache/babel/d515ddc0797ea256ff7271507737e9ce.json.gz +0 -0
  119. package/.cache/babel/d742848d263503f79168f1a6b8a52097.json.gz +0 -0
  120. package/.cache/babel/d834b41d7028519ecd0cbc8c65d33dde.json.gz +0 -0
  121. package/.cache/babel/d9060b9162e7caf974db0b90b6134727.json.gz +0 -0
  122. package/.cache/babel/d946975b9beac51a39f600461e84b1b6.json.gz +0 -0
  123. package/.cache/babel/daf0af780cff2270b1ae0e4b97a5a4ab.json.gz +0 -0
  124. package/.cache/babel/db5215643b0737dc5245972a5c25f736.json.gz +0 -0
  125. package/.cache/babel/dd57b8d31664b147b9f1e3f5cd18cc2a.json.gz +0 -0
  126. package/.cache/babel/e0c78bdaae19f15e2dd4a773e842422b.json.gz +0 -0
  127. package/.cache/babel/e1a2c7e4fc3afe9cc6e03d60f4e4a23d.json.gz +0 -0
  128. package/.cache/babel/e210ff9aa99df972b8d81c4bda70f642.json.gz +0 -0
  129. package/.cache/babel/e259a73f84d5b92b94e6d094719264c3.json.gz +0 -0
  130. package/.cache/babel/e6ce03a9908c9c4dc2c723531f1073f6.json.gz +0 -0
  131. package/.cache/babel/e8a1501dd8e4b3389ecdc8f722622359.json.gz +0 -0
  132. package/.cache/babel/eb3bb371036da362c42db50248d0b180.json.gz +0 -0
  133. package/.cache/babel/eb854e03c810bdb969497b2a36119bfa.json.gz +0 -0
  134. package/.cache/babel/ec25bb53826070b7f301e64f02355b8c.json.gz +0 -0
  135. package/.cache/babel/edbaca20e4a65f850c3d8321c1945718.json.gz +0 -0
  136. package/.cache/babel/f059f1ca9e49d5c3e655d20339860dce.json.gz +0 -0
  137. package/.cache/babel/f24c559fb42ef906405966598baf5f80.json.gz +0 -0
  138. package/.cache/babel/f2f333c3c546ad711d722d782decd450.json.gz +0 -0
  139. package/.cache/babel/f42c854ce8f1dc1a6e6fe7c510b033a5.json.gz +0 -0
  140. package/.cache/babel/f5211608f22c2e12594eafdbc4e66aa5.json.gz +0 -0
  141. package/.cache/babel/f645b8c88a49718cd778e3595212d4eb.json.gz +0 -0
  142. package/.cache/babel/f66dda5c4e4e9d04eb6b7411fcb12a65.json.gz +0 -0
  143. package/.cache/babel/f9d5b6f3f5438342dba75c2ce6143eef.json.gz +0 -0
  144. package/.cache/babel/fc9c60ee1d2e8783ba4c08db5cf97d40.json.gz +0 -0
  145. package/.cache/babel/fd558798cea72d04fb826468a89d72aa.json.gz +0 -0
  146. package/.cache/babel/fe735cbd0f6e131e28d8b6eb1f39141b.json.gz +0 -0
  147. package/.eslintrc.js +3 -26
  148. package/.nvmrc +1 -0
  149. package/CHANGELOG.md +422 -0
  150. package/babel.config.js +6 -0
  151. package/block-list.json +1 -1
  152. package/composer.json +2 -2
  153. package/composer.lock +446 -161
  154. package/dist/author-list/view.asset.php +1 -0
  155. package/dist/author-list/view.css +1 -0
  156. package/dist/author-list/view.js +1 -0
  157. package/dist/author-list/view.rtl.css +1 -0
  158. package/dist/author-profile/view.asset.php +1 -0
  159. package/dist/author-profile/view.css +1 -0
  160. package/dist/author-profile/view.js +1 -0
  161. package/dist/author-profile/view.rtl.css +1 -0
  162. package/dist/block_styles.asset.php +1 -0
  163. package/dist/block_styles.css +1 -0
  164. package/dist/block_styles.js +1 -0
  165. package/dist/block_styles.rtl.css +1 -0
  166. package/dist/carousel/view.asset.php +1 -0
  167. package/dist/carousel/view.css +1 -0
  168. package/dist/carousel/view.js +1 -0
  169. package/dist/carousel/view.rtl.css +1 -0
  170. package/dist/donate/view.asset.php +1 -0
  171. package/dist/donate/view.css +1 -0
  172. package/dist/donate/view.js +1 -0
  173. package/dist/donate/view.rtl.css +1 -0
  174. package/dist/donateStreamlined.asset.php +1 -0
  175. package/dist/donateStreamlined.css +1 -0
  176. package/dist/donateStreamlined.js +1 -0
  177. package/dist/donateStreamlined.rtl.css +1 -0
  178. package/dist/editor.asset.php +1 -0
  179. package/dist/editor.css +1 -0
  180. package/dist/editor.js +41 -0
  181. package/dist/editor.rtl.css +1 -0
  182. package/dist/homepage-articles/view.asset.php +1 -0
  183. package/dist/homepage-articles/view.css +1 -0
  184. package/dist/homepage-articles/view.js +1 -0
  185. package/dist/homepage-articles/view.rtl.css +1 -0
  186. package/dist/iframe/view.asset.php +1 -0
  187. package/dist/iframe/view.css +1 -0
  188. package/dist/iframe/view.js +1 -0
  189. package/dist/iframe/view.rtl.css +1 -0
  190. package/includes/class-newspack-blocks-api.php +14 -69
  191. package/includes/class-newspack-blocks-patterns.php +4 -1
  192. package/includes/class-newspack-blocks.php +421 -95
  193. package/newspack-blocks.php +26 -2
  194. package/package.json +31 -129
  195. package/postcss.config.js +11 -0
  196. package/src/block-patterns/subscribe-2.php +1 -1
  197. package/src/block-patterns/subscribe-3.php +1 -1
  198. package/src/block-patterns/subscribe-7.php +12 -0
  199. package/src/block-patterns/subscribe-8.php +12 -0
  200. package/src/block-patterns/subscribe-9.php +12 -0
  201. package/src/block-styles/core/columns/editor.scss +5 -8
  202. package/src/block-styles/core/columns/view.scss +83 -42
  203. package/src/blocks/author-list/block.json +82 -0
  204. package/src/blocks/author-list/class-wp-rest-newspack-author-list-controller.php +387 -0
  205. package/src/blocks/author-list/edit.js +527 -0
  206. package/src/blocks/author-list/editor.js +7 -0
  207. package/src/blocks/author-list/editor.scss +1 -0
  208. package/src/blocks/author-list/index.js +45 -0
  209. package/src/blocks/author-list/view.js +5 -0
  210. package/src/blocks/author-list/view.php +175 -0
  211. package/src/blocks/author-list/view.scss +46 -0
  212. package/src/blocks/author-profile/block.json +8 -0
  213. package/src/blocks/author-profile/class-wp-rest-newspack-authors-controller.php +69 -14
  214. package/src/blocks/author-profile/edit.js +212 -189
  215. package/src/blocks/author-profile/editor.scss +1 -1
  216. package/src/blocks/author-profile/single-author.js +100 -0
  217. package/src/blocks/author-profile/view.php +38 -92
  218. package/src/blocks/author-profile/view.scss +120 -1
  219. package/src/blocks/carousel/create-swiper.js +14 -5
  220. package/src/blocks/carousel/edit.js +4 -2
  221. package/src/blocks/carousel/editor.scss +3 -12
  222. package/src/blocks/carousel/view.js +1 -1
  223. package/src/blocks/carousel/view.php +9 -5
  224. package/src/blocks/carousel/view.scss +29 -3
  225. package/src/blocks/donate/class-wp-rest-newspack-donate-controller.php +22 -24
  226. package/src/blocks/donate/edit.js +49 -42
  227. package/src/blocks/donate/editor.scss +27 -3
  228. package/src/blocks/donate/index.js +31 -4
  229. package/src/blocks/donate/streamlined/index.js +248 -0
  230. package/src/blocks/donate/streamlined/index.test.js +125 -0
  231. package/src/blocks/donate/streamlined/style.scss +324 -0
  232. package/src/blocks/donate/streamlined/utils.js +174 -0
  233. package/src/blocks/donate/streamlined/utils.test.js +18 -0
  234. package/src/blocks/donate/view.php +77 -36
  235. package/src/blocks/donate/view.scss +291 -0
  236. package/src/blocks/homepage-articles/block.json +8 -0
  237. package/src/blocks/homepage-articles/class-wp-rest-newspack-articles-controller.php +24 -78
  238. package/src/blocks/homepage-articles/edit.js +26 -0
  239. package/src/blocks/homepage-articles/store.js +11 -1
  240. package/src/blocks/homepage-articles/templates/article.php +31 -3
  241. package/src/blocks/homepage-articles/templates/articles-loop.php +6 -5
  242. package/src/blocks/homepage-articles/utils.js +11 -10
  243. package/src/blocks/homepage-articles/view.php +4 -1
  244. package/src/blocks/homepage-articles/view.scss +69 -24
  245. package/src/blocks/iframe/block.json +34 -0
  246. package/src/blocks/iframe/class-wp-rest-newspack-iframe-controller.php +345 -0
  247. package/src/blocks/iframe/edit.js +252 -0
  248. package/src/blocks/iframe/editor.js +7 -0
  249. package/src/blocks/iframe/editor.scss +84 -0
  250. package/src/blocks/iframe/icons.js +10 -0
  251. package/src/blocks/iframe/iframe-placeholder.js +180 -0
  252. package/src/blocks/iframe/index.js +48 -0
  253. package/src/blocks/iframe/view.js +5 -0
  254. package/src/blocks/iframe/view.php +130 -0
  255. package/src/blocks/iframe/view.scss +9 -0
  256. package/src/blocks/video-playlist/view.php +4 -3
  257. package/src/components/query-controls.js +5 -4
  258. package/src/setup/editor.js +0 -1
  259. package/src/shared/js/utils.js +1 -1
  260. package/src/templates/author-profile-card.php +99 -0
  261. package/vendor/autoload.php +7 -0
  262. package/vendor/composer/ClassLoader.php +572 -0
  263. package/vendor/composer/InstalledVersions.php +350 -0
  264. package/vendor/composer/LICENSE +21 -0
  265. package/vendor/composer/autoload_classmap.php +10 -0
  266. package/vendor/composer/autoload_namespaces.php +9 -0
  267. package/vendor/composer/autoload_psr4.php +9 -0
  268. package/vendor/composer/autoload_real.php +55 -0
  269. package/vendor/composer/autoload_static.php +20 -0
  270. package/vendor/composer/installed.json +5 -0
  271. package/vendor/composer/installed.php +23 -0
  272. package/webpack.config.js +2 -3
  273. package/.circleci/config.yml +0 -96
  274. package/.distignore +0 -36
  275. package/.editorconfig +0 -22
  276. package/.github/ISSUE_TEMPLATE/Bug_report.md +0 -21
  277. package/.github/ISSUE_TEMPLATE/Enhancement.md +0 -17
  278. package/.github/ISSUE_TEMPLATE/Feature_request.md +0 -17
  279. package/.github/PULL_REQUEST_TEMPLATE.md +0 -29
  280. package/.github/workflows/main.yml +0 -15
  281. package/.phpcs.xml.dist +0 -55
  282. package/.prettierrc +0 -10
  283. package/.stylelintrc +0 -21
  284. package/.travis.yml +0 -67
  285. package/phpunit.xml.dist +0 -16
  286. package/src/blocks/author-profile/shared.scss +0 -95
  287. package/src/blocks/donate/streamlined.js +0 -180
  288. package/src/blocks/donate/streamlined.scss +0 -124
  289. package/src/setup/public-path.js +0 -13
  290. package/src/setup/view.js +0 -4
  291. package/tests/bootstrap.php +0 -31
  292. package/tests/test-sample.php +0 -20
@@ -8,7 +8,7 @@ import classNames from 'classnames';
8
8
  */
9
9
  import { __, sprintf } from '@wordpress/i18n';
10
10
  import apiFetch from '@wordpress/api-fetch';
11
- import { Component, Fragment } from '@wordpress/element';
11
+ import { Component } from '@wordpress/element';
12
12
  import {
13
13
  PanelBody,
14
14
  ExternalLink,
@@ -320,12 +320,16 @@ class Edit extends Component {
320
320
  const { buttonText } = attributes;
321
321
  return (
322
322
  <button type="submit" onClick={ evt => evt.preventDefault() }>
323
- <RichText
324
- onChange={ value => setAttributes( { buttonText: value } ) }
325
- placeholder={ __( 'Button text…', 'newspack-blocks' ) }
326
- value={ buttonText }
327
- tagName="span"
328
- />
323
+ { this.isRenderingStreamlinedBlock() ? (
324
+ __( 'Donate with card', 'newspack-blocks' )
325
+ ) : (
326
+ <RichText
327
+ onChange={ value => setAttributes( { buttonText: value } ) }
328
+ placeholder={ __( 'Button text…', 'newspack-blocks' ) }
329
+ value={ buttonText }
330
+ tagName="span"
331
+ />
332
+ ) }
329
333
  </button>
330
334
  );
331
335
  }
@@ -346,7 +350,12 @@ class Edit extends Component {
346
350
  { this.isRenderingStreamlinedBlock() ? (
347
351
  <div className="wp-block-newspack-blocks-donate__stripe stripe-payment">
348
352
  <div className="stripe-payment__row stripe-payment__row--flex stripe-payment__footer">
349
- { this.renderButton() }
353
+ <div className="stripe-payment__methods">
354
+ <div className="stripe-payment__request-button">
355
+ { __( 'Apple/Google Pay Button', 'newspack-blocks' ) }
356
+ </div>
357
+ { this.renderButton() }
358
+ </div>
350
359
  <a
351
360
  target="_blank"
352
361
  rel="noreferrer"
@@ -421,7 +430,7 @@ class Edit extends Component {
421
430
  const { setAttributes } = this.props;
422
431
  const { currencySymbol, suggestedAmounts, suggestedAmountUntiered, tiered } = this.blockData();
423
432
  return (
424
- <Fragment>
433
+ <>
425
434
  <ToggleControl
426
435
  key="tiered"
427
436
  checked={ tiered }
@@ -429,7 +438,7 @@ class Edit extends Component {
429
438
  label={ __( 'Tiered', 'newspack-blocks' ) }
430
439
  />
431
440
  { tiered && (
432
- <Fragment>
441
+ <>
433
442
  <TextControl
434
443
  key="low-tier"
435
444
  /* Translators: %s: the symbol for the current currency */
@@ -480,7 +489,7 @@ class Edit extends Component {
480
489
  } )
481
490
  }
482
491
  />
483
- </Fragment>
492
+ </>
484
493
  ) }
485
494
  { ! tiered && (
486
495
  <TextControl
@@ -496,7 +505,7 @@ class Edit extends Component {
496
505
  }
497
506
  />
498
507
  ) }
499
- </Fragment>
508
+ </>
500
509
  );
501
510
  }
502
511
 
@@ -504,38 +513,11 @@ class Edit extends Component {
504
513
  const { setAttributes } = this.props;
505
514
  const { manual, campaign, defaultFrequency } = this.blockData();
506
515
  return (
507
- <Fragment>
516
+ <>
508
517
  { this.renderPlaceholder() }
509
518
  { this.renderForm() }
510
519
  <InspectorControls>
511
- <PanelBody>
512
- <ToggleControl
513
- key="manual"
514
- checked={ manual }
515
- onChange={ this.manualChanged }
516
- label={ __( 'Configure manually', 'newspack-blocks' ) }
517
- />
518
- { ! manual && (
519
- <Fragment>
520
- <p>
521
- { __(
522
- 'The Donate Block allows you to collect donations from readers. The fields are automatically defined based on your donation settings.',
523
- 'newspack-blocks'
524
- ) }
525
- </p>
526
-
527
- <ExternalLink href="/wp-admin/admin.php?page=newspack-reader-revenue-wizard#/donations">
528
- { __( 'Edit donation settings.', 'newspack-blocks' ) }
529
- </ExternalLink>
530
- </Fragment>
531
- ) }
532
- </PanelBody>
533
- { manual && (
534
- <PanelBody title={ __( 'Manual Settings', 'newspack-blocks' ) }>
535
- { this.renderManualControls() }
536
- </PanelBody>
537
- ) }
538
- <PanelBody>
520
+ <PanelBody title={ __( 'Donate Settings', 'newspack-blocks' ) }>
539
521
  <SelectControl
540
522
  label={ __( 'Default Tab', 'newspack' ) }
541
523
  value={ defaultFrequency }
@@ -559,7 +541,32 @@ class Edit extends Component {
559
541
  } );
560
542
  } }
561
543
  />
544
+ <ToggleControl
545
+ key="manual"
546
+ checked={ manual }
547
+ onChange={ this.manualChanged }
548
+ label={ __( 'Configure manually', 'newspack-blocks' ) }
549
+ />
550
+ { ! manual && (
551
+ <>
552
+ <p>
553
+ { __(
554
+ 'The Donate Block allows you to collect donations from readers. The fields are automatically defined based on your donation settings.',
555
+ 'newspack-blocks'
556
+ ) }
557
+ </p>
558
+
559
+ <ExternalLink href="/wp-admin/admin.php?page=newspack-reader-revenue-wizard#/donations">
560
+ { __( 'Edit donation setting', 'newspack-blocks' ) }
561
+ </ExternalLink>
562
+ </>
563
+ ) }
562
564
  </PanelBody>
565
+ { manual && (
566
+ <PanelBody title={ __( 'Donation Amount', 'newspack-blocks' ) }>
567
+ { this.renderManualControls() }
568
+ </PanelBody>
569
+ ) }
563
570
  <PanelBody title={ __( 'Campaign', 'newspack-blocks' ) } initialOpen={ false }>
564
571
  <TextControl
565
572
  label={ __( 'Campaign ID', 'newspack-blocks' ) }
@@ -572,7 +579,7 @@ class Edit extends Component {
572
579
  />
573
580
  </PanelBody>
574
581
  </InspectorControls>
575
- </Fragment>
582
+ </>
576
583
  );
577
584
  }
578
585
  }
@@ -1,11 +1,10 @@
1
1
  @import '../../shared/sass/colors';
2
2
  @import '../../shared/sass/variables';
3
3
 
4
- @import './streamlined.scss';
4
+ @import './streamlined/style.scss';
5
5
 
6
6
  .wp-block-newspack-blocks-donate {
7
7
  button[type='submit'] {
8
- background: $color__secondary;
9
8
  border: none;
10
9
  border-radius: 5px;
11
10
  box-sizing: border-box;
@@ -13,8 +12,33 @@
13
12
  font-size: $font__size-sm;
14
13
  font-weight: bold;
15
14
  outline: none;
16
- line-height: 3em;
15
+ line-height: 3;
17
16
  padding-left: 20px;
18
17
  padding-right: 20px;
19
18
  }
19
+ .stripe-payment {
20
+ &__methods {
21
+ > * {
22
+ line-height: 46px !important;
23
+ }
24
+ }
25
+ &__request-button {
26
+ font-size: 0.8em;
27
+ border-radius: 5px;
28
+ color: $color__secondary;
29
+ border: $color__secondary 1px dashed;
30
+ padding: 0 20px;
31
+ opacity: 0.5;
32
+ }
33
+ }
34
+ }
35
+
36
+ .block-editor-block-list__layout
37
+ .block-editor-block-list__block
38
+ .wp-block-newspack-blocks-donate.tiered.is-style-minimal
39
+ .wp-block-newspack-blocks-donate__tiers
40
+ input[type='radio']:checked
41
+ + .tier-select-label {
42
+ background: transparent;
43
+ color: $color__text-main;
20
44
  }
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
+ import { ExternalLink } from '@wordpress/components';
4
5
  import { __ } from '@wordpress/i18n';
5
- import { Icon, payment } from '@wordpress/icons';
6
+ import { registerBlockStyle } from '@wordpress/blocks';
7
+ import { payment } from '@wordpress/icons';
6
8
 
7
9
  /**
8
10
  * Internal dependencies
@@ -21,7 +23,7 @@ export const title = __( 'Donate', 'newspack-blocks' );
21
23
  export const settings = {
22
24
  title,
23
25
  icon: {
24
- src: <Icon icon={ payment } />,
26
+ src: payment,
25
27
  foreground: '#36f',
26
28
  },
27
29
  category: 'newspack',
@@ -30,7 +32,19 @@ export const settings = {
30
32
  __( 'memberships', 'newspack-blocks' ),
31
33
  __( 'subscriptions', 'newspack-blocks' ),
32
34
  ],
33
- description: __( 'Enable donations.', 'newspack-blocks' ),
35
+ description: (
36
+ <>
37
+ <p>
38
+ { __(
39
+ 'Manually place a donation block on any post or page on your site.',
40
+ 'newspack-blocks'
41
+ ) }
42
+ </p>
43
+ <ExternalLink href={ __( 'https://newspack.pub/support/blocks/donate-block/' ) }>
44
+ { __( 'Support reference', 'newspack-blocks' ) }
45
+ </ExternalLink>
46
+ </>
47
+ ),
34
48
  attributes: {
35
49
  className: {
36
50
  type: 'string',
@@ -59,7 +73,7 @@ export const settings = {
59
73
  },
60
74
  buttonText: {
61
75
  type: 'string',
62
- default: __( 'Donate now!', 'newspack-blocks' ),
76
+ default: __( 'Donate Now', 'newspack-blocks' ),
63
77
  },
64
78
  defaultFrequency: {
65
79
  type: 'string',
@@ -73,3 +87,16 @@ export const settings = {
73
87
  edit,
74
88
  save: () => null, // to use view.php
75
89
  };
90
+
91
+ /**
92
+ * Block Styles
93
+ */
94
+ registerBlockStyle( 'newspack-blocks/donate', {
95
+ name: 'alternate',
96
+ label: __( 'Alternate', 'newapack-blocks' ),
97
+ } );
98
+
99
+ registerBlockStyle( 'newspack-blocks/donate', {
100
+ name: 'minimal',
101
+ label: __( 'Minimal', 'newapack-blocks' ),
102
+ } );
@@ -0,0 +1,248 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __ } from '@wordpress/i18n';
5
+
6
+ /**
7
+ * External dependencies
8
+ */
9
+ import { loadStripe } from '@stripe/stripe-js/pure';
10
+ import 'regenerator-runtime'; // Required in WP >=5.8.
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import * as utils from './utils';
16
+ import './style.scss';
17
+
18
+ export const processStreamlinedElements = ( parentElement = document ) =>
19
+ [ ...parentElement.querySelectorAll( '.stripe-payment' ) ].forEach( async el => {
20
+ let stripe, cardElement, paymentRequest, paymentMethod, paymentRequestToken;
21
+
22
+ const formElement = el.closest( 'form' );
23
+ const messagesEl = el.querySelector( '.stripe-payment__messages' );
24
+
25
+ const settings = utils.getSettings( formElement );
26
+
27
+ const disableForm = () => el.classList.add( 'stripe-payment--disabled' );
28
+ const enableForm = () => el.classList.remove( 'stripe-payment--disabled' );
29
+ enableForm();
30
+
31
+ // Universal payment handling, for both card and payment request button flows.
32
+ // In card flow, this will happen after user submits their card data in the HTML form.
33
+ // In payment request flow, this will happen after the user validates the payment in
34
+ // the browser/OS UI.
35
+ const payWithToken = async (
36
+ token,
37
+ /**
38
+ * Overrides for sent donation data. In a card flow the data is
39
+ * provided explicitly by the user (via the form), but in payment request flow
40
+ * the data is from the payment request event.
41
+ */
42
+ requestPayloadOverrides = {}
43
+ ) => {
44
+ const formValues = utils.getFormValues( formElement );
45
+ const apiRequestPayload = {
46
+ tokenData: token,
47
+ amount: utils.getTotalAmount( formElement ),
48
+ email: formValues.email,
49
+ full_name: formValues.full_name,
50
+ frequency: formValues.donation_frequency,
51
+ newsletter_opt_in: Boolean( formValues.newsletter_opt_in ),
52
+ clientId: formValues.cid,
53
+ ...requestPayloadOverrides,
54
+ };
55
+ const chargeResultData = await utils.sendAPIRequest( '/donate', apiRequestPayload );
56
+
57
+ // Error handling.
58
+ if ( chargeResultData.data?.status !== 200 && chargeResultData.message ) {
59
+ utils.renderMessages( [ chargeResultData.message ], messagesEl );
60
+ return { error: true };
61
+ }
62
+ if ( chargeResultData.error ) {
63
+ utils.renderMessages( [ chargeResultData.error ], messagesEl );
64
+ return { error: true };
65
+ }
66
+
67
+ const exitWithError = errorMessage => {
68
+ utils.renderMessages( [ errorMessage ], messagesEl );
69
+ enableForm();
70
+ return { error: true };
71
+ };
72
+
73
+ // Additional steps handling.
74
+ if ( chargeResultData.client_secret && paymentMethod ) {
75
+ const confirmationResult = await stripe.confirmCardPayment(
76
+ chargeResultData.client_secret,
77
+ {
78
+ payment_method: paymentMethod,
79
+ }
80
+ );
81
+
82
+ if ( confirmationResult.error ) {
83
+ return exitWithError( confirmationResult.error.message );
84
+ } else if ( confirmationResult.paymentIntent.status === 'succeeded' ) {
85
+ utils.renderSuccessMessageWithEmail( apiRequestPayload.email, messagesEl );
86
+ } else if ( confirmationResult.paymentIntent.status === 'requires_action' ) {
87
+ // Could not test this flow (with 3D Secure test cards), but it's listed in the docs:
88
+ // https://stripe.com/docs/stripe-js/elements/payment-request-button#html-js-complete-payment
89
+ const { error: confirmationError } = await stripe.confirmCardPayment(
90
+ chargeResultData.client_secret
91
+ );
92
+ if ( confirmationError ) {
93
+ return exitWithError( confirmationError.message );
94
+ }
95
+ utils.renderSuccessMessageWithEmail( apiRequestPayload.email, messagesEl );
96
+ } else {
97
+ return exitWithError(
98
+ __(
99
+ 'Something went wrong with the payment. Please try again later.',
100
+ 'newspack-blocks'
101
+ )
102
+ );
103
+ }
104
+ }
105
+
106
+ if ( chargeResultData.status === 'success' ) {
107
+ utils.renderSuccessMessageWithEmail( apiRequestPayload.email, messagesEl );
108
+ }
109
+ return {};
110
+ };
111
+
112
+ const initStripe = async () => {
113
+ stripe = await loadStripe( settings.stripePublishableKey );
114
+
115
+ const elements = stripe.elements();
116
+
117
+ // Handle card element.
118
+ cardElement = elements.create( 'card' );
119
+ cardElement.mount( el.querySelector( '.stripe-payment__card' ) );
120
+
121
+ // Handle payment request button (Apple/Google Pay). This has to be initialised to see if
122
+ // such payments are available in the browser (canMakePayment method).
123
+ paymentRequest = stripe.paymentRequest( {
124
+ country: settings.countryCode,
125
+ currency: settings.currency,
126
+ total: utils.getPaymentRequestTotal( formElement ),
127
+ // Docs: Use the requestPayerName parameter to collect the payer’s billing address for Apple Pay.
128
+ requestPayerName: true,
129
+ requestPayerEmail: true,
130
+ } );
131
+
132
+ const rendersPaymentRequestButton = await paymentRequest.canMakePayment();
133
+ if ( rendersPaymentRequestButton ) {
134
+ paymentRequest.on( 'token', async event => {
135
+ paymentRequestToken = event.token;
136
+ } );
137
+ paymentRequest.on( 'paymentmethod', async event => {
138
+ // Save payment method ID to use it in payWithToken if a client secret is returned.
139
+ paymentMethod = event.paymentMethod.id;
140
+ const result = await payWithToken( paymentRequestToken, {
141
+ email: event.payerEmail,
142
+ full_name: event.payerName,
143
+ payment_method_id: paymentMethod,
144
+ } );
145
+ // The UI messages are handled in payWithToken, this event listener only
146
+ // has to notify the browser that the payment is done.
147
+ event.complete( result.error ? 'fail' : 'success' );
148
+ } );
149
+ // Update payment request when the form values are updated.
150
+ formElement.addEventListener( 'change', () => {
151
+ paymentRequest.update( {
152
+ total: utils.getPaymentRequestTotal( formElement ),
153
+ } );
154
+ } );
155
+ // Create and mount the payment request button.
156
+ const prButton = elements.create( 'paymentRequestButton', {
157
+ paymentRequest,
158
+ style: {
159
+ paymentRequestButton: {
160
+ type: 'donate',
161
+ height: '46px',
162
+ },
163
+ },
164
+ } );
165
+ const prButtonElement = el.querySelector( '.stripe-payment__request-button' );
166
+ prButton.mount( prButtonElement );
167
+ prButtonElement.classList.remove( 'stripe-payment--hidden' );
168
+ setTimeout( () => {
169
+ prButtonElement.classList.remove( 'stripe-payment__request-button--invisible' );
170
+ }, 0 );
171
+ }
172
+
173
+ el.classList.remove( 'stripe-payment--invisible' );
174
+ };
175
+
176
+ initStripe();
177
+
178
+ // Card form unravelling.
179
+ const submitButtonEl = el.querySelector( 'button[type="submit"]' );
180
+ submitButtonEl.onclick = e => {
181
+ const inputsHiddenEl = el.querySelector( '.stripe-payment__inputs.stripe-payment--hidden' );
182
+ if ( inputsHiddenEl ) {
183
+ e.preventDefault();
184
+ inputsHiddenEl.classList.remove( 'stripe-payment--hidden' );
185
+ }
186
+ };
187
+
188
+ const updateFeesAmount = () => {
189
+ const feesAmountEl = el.querySelector( '#stripe-fees-amount' );
190
+ if ( feesAmountEl ) {
191
+ const formValues = Object.fromEntries( new FormData( formElement ) );
192
+ const feeAmount = utils.getFeeAmount( formElement );
193
+ feesAmountEl.innerHTML = `(${ settings.currencySymbol }${ feeAmount.toFixed(
194
+ 2
195
+ ) } ${ settings.frequencies[ formValues.donation_frequency ].toLowerCase() })`;
196
+ }
197
+ };
198
+
199
+ updateFeesAmount();
200
+ formElement.addEventListener( 'change', updateFeesAmount );
201
+
202
+ // Card payment flow – on form submission.
203
+ formElement.addEventListener( 'submit', async e => {
204
+ e.preventDefault();
205
+ disableForm();
206
+ utils.renderMessages(
207
+ [ __( 'Processing payment…', 'newspack-blocks' ) ],
208
+ messagesEl,
209
+ 'info'
210
+ );
211
+
212
+ const formValues = utils.getFormValues( formElement );
213
+ const validationErrors = Object.values( utils.validateFormData( formValues ) );
214
+ if ( validationErrors.length > 0 ) {
215
+ utils.renderMessages( validationErrors, messagesEl );
216
+ enableForm();
217
+ return;
218
+ }
219
+
220
+ const handleStripeSDKError = error => {
221
+ validationErrors.push( error.message );
222
+ utils.renderMessages( validationErrors, messagesEl );
223
+ enableForm();
224
+ };
225
+
226
+ const stripeTokenCreationResult = await stripe.createToken( cardElement );
227
+ if ( stripeTokenCreationResult.error ) {
228
+ handleStripeSDKError( stripeTokenCreationResult.error );
229
+ return;
230
+ }
231
+ const paymentMethodCreationResult = await stripe.createPaymentMethod( {
232
+ type: 'card',
233
+ card: cardElement,
234
+ billing_details: { name: formValues.full_name, email: formValues.email },
235
+ } );
236
+ if ( paymentMethodCreationResult.error ) {
237
+ handleStripeSDKError( paymentMethodCreationResult.error );
238
+ return;
239
+ }
240
+ // Save payment method ID to use it in payWithToken if a client secret is returned.
241
+ paymentMethod = { card: cardElement };
242
+ await payWithToken( stripeTokenCreationResult.token, {
243
+ payment_method_id: paymentMethodCreationResult.paymentMethod.id,
244
+ } );
245
+ } );
246
+ } );
247
+
248
+ processStreamlinedElements();
@@ -0,0 +1,125 @@
1
+ import * as testingLibrary from '@testing-library/dom';
2
+ import userEvent from '@testing-library/user-event';
3
+ import fetchMock from 'fetch-mock-jest';
4
+ import { encode } from 'html-entities';
5
+
6
+ import { processStreamlinedElements } from '.';
7
+
8
+ const MONTHLY_AMOUNT = 7;
9
+
10
+ const createDOM = settings => {
11
+ const parentElement = document.createElement( 'div' );
12
+ parentElement.innerHTML = `
13
+ <style>.stripe-payment--hidden {display:none;}</style>
14
+ <form data-settings="${ encode( JSON.stringify( settings ) ) }">
15
+ <div class='frequencies'>
16
+ <div class='frequency'>
17
+ <input type="radio" value="once" id="once" name="donation_frequency">
18
+ <label for="once">Once</label>
19
+ <input type="number" name="donation_value_once" value="${ MONTHLY_AMOUNT * 12 }" />
20
+ </div>
21
+ <div class='frequency'>
22
+ <input type="radio" value="month" id="month" name="donation_frequency" checked>
23
+ <label for="month">Monthly</label>
24
+ <input type="number" name="donation_value_month" value="${ MONTHLY_AMOUNT }" />
25
+ </div>
26
+ </div>
27
+ <div class="stripe-payment">
28
+ <div class="stripe-payment__inputs stripe-payment--hidden">
29
+ <input required="" placeholder="Email" type="email" name="email" value="">
30
+ <input required="" placeholder="Full Name" type="text" name="full_name" value="">
31
+ </div>
32
+ <label>
33
+ <input type="checkbox" name="agree_to_pay_fees" checked value="true">Agree to pay fees?
34
+ <span id="stripe-fees-amount">($0)</span>
35
+ </label>
36
+ <div class="stripe-payment__messages"></div>
37
+ <button type="submit">Donate</button>
38
+ </div>
39
+ <input name="cid" type="hidden" value="amp-123" />
40
+ </form>
41
+ `;
42
+ document.body.appendChild( parentElement );
43
+ return document.body;
44
+ };
45
+
46
+ fetchMock.post( '/wp-json/newspack-blocks/v1/donate', () => {
47
+ return { status: 'success', client_secret: 'sec_123' };
48
+ } );
49
+
50
+ const frequencies = { once: 'Once', month: 'Monthly', year: 'Annually' };
51
+ const feeMultiplier = '2.9';
52
+ const feeStatic = '0.3';
53
+ const settings = [ 'USD', '$', 'Testing Site', false, 'US', frequencies, feeMultiplier, feeStatic ];
54
+
55
+ describe( 'Streamlined Donate block processing', () => {
56
+ const container = createDOM( settings );
57
+ processStreamlinedElements( container );
58
+
59
+ const button = testingLibrary.getByText( container, 'Donate' );
60
+ const emailInput = testingLibrary.getByPlaceholderText( container, 'Email' );
61
+ const nameInput = testingLibrary.getByPlaceholderText( container, 'Full Name' );
62
+
63
+ it( 'additional inputs are initially hidden and displayed after user clicks the button', async () => {
64
+ expect( emailInput ).not.toBeVisible();
65
+ await userEvent.click( button );
66
+ expect( emailInput ).toBeVisible();
67
+ } );
68
+
69
+ it( 'form submission with invalid values triggers validation errors', async () => {
70
+ await userEvent.click( button );
71
+ expect(
72
+ testingLibrary.getByText( container, 'Email address is invalid.' )
73
+ ).toBeInTheDocument();
74
+ expect(
75
+ testingLibrary.getByText( container, 'Full name should be provided.' )
76
+ ).toBeInTheDocument();
77
+
78
+ await userEvent.type( emailInput, 'foo@bar.com' );
79
+ await userEvent.click( button );
80
+ expect(
81
+ testingLibrary.queryByText( container, 'Email address is invalid.' )
82
+ ).not.toBeInTheDocument();
83
+ } );
84
+
85
+ it( 'the fee amount is updated', () => {
86
+ expect( testingLibrary.getByText( container, '($0.52 monthly)' ) ).toBeInTheDocument();
87
+ } );
88
+
89
+ it( 'form can be submitted after validation passes', async () => {
90
+ await userEvent.type( nameInput, 'Bax' );
91
+ await userEvent.click( button );
92
+ expect(
93
+ testingLibrary.queryByText( container, 'Full name should be provided.' )
94
+ ).not.toBeInTheDocument();
95
+ expect( testingLibrary.getByText( container, 'Processing payment…' ) ).toBeInTheDocument();
96
+ } );
97
+
98
+ it( 'final success message is displayed', () => {
99
+ expect(
100
+ testingLibrary.getByText(
101
+ container,
102
+ 'Your payment has been processed. Thank you for your contribution! You will receive a confirmation email at foo@bar.com.'
103
+ )
104
+ ).toBeInTheDocument();
105
+ } );
106
+
107
+ it( 'correct payload was sent to the API', () => {
108
+ expect( fetchMock ).toHaveLastFetched(
109
+ '/wp-json/newspack-blocks/v1/donate',
110
+ {
111
+ body: {
112
+ tokenData: 'abc',
113
+ amount: 7.52,
114
+ email: 'foo@bar.com',
115
+ full_name: 'Bax',
116
+ frequency: 'month',
117
+ newsletter_opt_in: false,
118
+ clientId: 'amp-123',
119
+ payment_method_id: 'pm_123',
120
+ },
121
+ },
122
+ 'post'
123
+ );
124
+ } );
125
+ } );