@automattic/plans-grid-next 1.0.1 → 1.0.3

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 (339) hide show
  1. package/CHANGELOG.md +4 -1
  2. package/dist/cjs/_shared.scss +4 -3
  3. package/dist/cjs/components/comparison-grid/index.js +101 -71
  4. package/dist/cjs/components/comparison-grid/index.js.map +1 -1
  5. package/dist/cjs/components/comparison-grid/index.stories.js.map +1 -1
  6. package/dist/cjs/components/comparison-grid/style.scss +10 -2
  7. package/dist/cjs/components/features-grid/client-logo-list/client-list.js +0 -12
  8. package/dist/cjs/components/features-grid/client-logo-list/client-list.js.map +1 -1
  9. package/dist/cjs/components/features-grid/index.js +9 -6
  10. package/dist/cjs/components/features-grid/index.js.map +1 -1
  11. package/dist/cjs/components/features-grid/plan-features-list.js +10 -3
  12. package/dist/cjs/components/features-grid/plan-features-list.js.map +1 -1
  13. package/dist/cjs/components/features-grid/plan-headers.js +2 -2
  14. package/dist/cjs/components/features-grid/plan-headers.js.map +1 -1
  15. package/dist/cjs/components/features-grid/plan-tagline.js +1 -1
  16. package/dist/cjs/components/features-grid/plan-tagline.js.map +1 -1
  17. package/dist/cjs/components/features-grid/style.scss +111 -21
  18. package/dist/cjs/components/features-grid/table.js +1 -1
  19. package/dist/cjs/components/features-grid/table.js.map +1 -1
  20. package/dist/cjs/components/features.js +43 -4
  21. package/dist/cjs/components/features.js.map +1 -1
  22. package/dist/cjs/components/item.js +1 -1
  23. package/dist/cjs/components/item.js.map +1 -1
  24. package/dist/cjs/components/plan-button/index.js +5 -3
  25. package/dist/cjs/components/plan-button/index.js.map +1 -1
  26. package/dist/cjs/components/plan-button/style.scss +71 -47
  27. package/dist/cjs/components/plan-div-td-container.js +4 -1
  28. package/dist/cjs/components/plan-div-td-container.js.map +1 -1
  29. package/dist/cjs/components/plan-logo.js +6 -3
  30. package/dist/cjs/components/plan-logo.js.map +1 -1
  31. package/dist/cjs/components/plan-type-selector/components/interval-type-dropdown.js +12 -1
  32. package/dist/cjs/components/plan-type-selector/components/interval-type-dropdown.js.map +1 -1
  33. package/dist/cjs/components/plan-type-selector/hooks/use-max-discount.js +4 -33
  34. package/dist/cjs/components/plan-type-selector/hooks/use-max-discount.js.map +1 -1
  35. package/dist/cjs/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.js +11 -13
  36. package/dist/cjs/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.js.map +1 -1
  37. package/dist/cjs/components/plans-2023-tooltip.js +16 -5
  38. package/dist/cjs/components/plans-2023-tooltip.js.map +1 -1
  39. package/dist/cjs/components/shared/action-button/index.js +22 -7
  40. package/dist/cjs/components/shared/action-button/index.js.map +1 -1
  41. package/dist/cjs/components/shared/action-button/style.scss +4 -0
  42. package/dist/cjs/components/shared/billing-timeframe/index.js +8 -4
  43. package/dist/cjs/components/shared/billing-timeframe/index.js.map +1 -1
  44. package/dist/cjs/components/shared/header-price/index.js +60 -15
  45. package/dist/cjs/components/shared/header-price/index.js.map +1 -1
  46. package/dist/cjs/components/shared/header-price/style.scss +10 -2
  47. package/dist/cjs/components/shared/storage/components/plan-storage.js +2 -2
  48. package/dist/cjs/components/shared/storage/components/plan-storage.js.map +1 -1
  49. package/dist/cjs/components/shared/storage/components/storage-dropdown.js +29 -6
  50. package/dist/cjs/components/shared/storage/components/storage-dropdown.js.map +1 -1
  51. package/dist/cjs/components/shared/storage/components/storage-feature-label.js +2 -1
  52. package/dist/cjs/components/shared/storage/components/storage-feature-label.js.map +1 -1
  53. package/dist/cjs/components/shared/storage/hooks/use-plan-storage.js +2 -0
  54. package/dist/cjs/components/shared/storage/hooks/use-plan-storage.js.map +1 -1
  55. package/dist/cjs/fixtures/sites-purchases.js +2 -4
  56. package/dist/cjs/fixtures/sites-purchases.js.map +1 -1
  57. package/dist/cjs/grid-context.js +4 -1
  58. package/dist/cjs/grid-context.js.map +1 -1
  59. package/dist/cjs/hooks/data-store/get-renewal-pricing-text.js +50 -0
  60. package/dist/cjs/hooks/data-store/get-renewal-pricing-text.js.map +1 -0
  61. package/dist/cjs/hooks/data-store/use-grid-plans-for-comparison-grid.js +6 -1
  62. package/dist/cjs/hooks/data-store/use-grid-plans-for-comparison-grid.js.map +1 -1
  63. package/dist/cjs/hooks/data-store/use-grid-plans-for-features-grid.js +6 -1
  64. package/dist/cjs/hooks/data-store/use-grid-plans-for-features-grid.js.map +1 -1
  65. package/dist/cjs/hooks/data-store/use-grid-plans.js +175 -21
  66. package/dist/cjs/hooks/data-store/use-grid-plans.js.map +1 -1
  67. package/dist/cjs/hooks/data-store/use-highlight-labels.js +13 -4
  68. package/dist/cjs/hooks/data-store/use-highlight-labels.js.map +1 -1
  69. package/dist/cjs/hooks/data-store/use-plan-billing-description.js +68 -13
  70. package/dist/cjs/hooks/data-store/use-plan-billing-description.js.map +1 -1
  71. package/dist/cjs/hooks/data-store/use-plan-billing-period.js +14 -0
  72. package/dist/cjs/hooks/data-store/use-plan-billing-period.js.map +1 -0
  73. package/dist/cjs/hooks/data-store/use-plan-features-for-grid-plans.js +76 -2
  74. package/dist/cjs/hooks/data-store/use-plan-features-for-grid-plans.js.map +1 -1
  75. package/dist/cjs/hooks/data-store/use-restructured-plan-features-for-comparison-grid.js +60 -12
  76. package/dist/cjs/hooks/data-store/use-restructured-plan-features-for-comparison-grid.js.map +1 -1
  77. package/dist/cjs/hooks/data-store/use-title-badges.js +19 -0
  78. package/dist/cjs/hooks/data-store/use-title-badges.js.map +1 -0
  79. package/dist/cjs/hooks/use-grid-size.js.map +1 -1
  80. package/dist/cjs/hooks/use-is-large-currency.js +2 -2
  81. package/dist/cjs/hooks/use-is-large-currency.js.map +1 -1
  82. package/dist/cjs/hooks/use-visible-grid-plans.js +70 -0
  83. package/dist/cjs/hooks/use-visible-grid-plans.js.map +1 -0
  84. package/dist/cjs/index.js +8 -1
  85. package/dist/cjs/index.js.map +1 -1
  86. package/dist/cjs/lib/get-plan-features-object.js +15 -2
  87. package/dist/cjs/lib/get-plan-features-object.js.map +1 -1
  88. package/dist/cjs/lib/plan-pricing-utils.js +135 -0
  89. package/dist/cjs/lib/plan-pricing-utils.js.map +1 -0
  90. package/dist/esm/_shared.scss +4 -3
  91. package/dist/esm/components/comparison-grid/index.js +102 -72
  92. package/dist/esm/components/comparison-grid/index.js.map +1 -1
  93. package/dist/esm/components/comparison-grid/index.stories.js.map +1 -1
  94. package/dist/esm/components/comparison-grid/style.scss +10 -2
  95. package/dist/esm/components/features-grid/client-logo-list/client-list.js +0 -12
  96. package/dist/esm/components/features-grid/client-logo-list/client-list.js.map +1 -1
  97. package/dist/esm/components/features-grid/index.js +9 -6
  98. package/dist/esm/components/features-grid/index.js.map +1 -1
  99. package/dist/esm/components/features-grid/plan-features-list.js +10 -3
  100. package/dist/esm/components/features-grid/plan-features-list.js.map +1 -1
  101. package/dist/esm/components/features-grid/plan-headers.js +3 -3
  102. package/dist/esm/components/features-grid/plan-headers.js.map +1 -1
  103. package/dist/esm/components/features-grid/plan-tagline.js +1 -1
  104. package/dist/esm/components/features-grid/plan-tagline.js.map +1 -1
  105. package/dist/esm/components/features-grid/style.scss +111 -21
  106. package/dist/esm/components/features-grid/table.js +1 -1
  107. package/dist/esm/components/features-grid/table.js.map +1 -1
  108. package/dist/esm/components/features.js +44 -5
  109. package/dist/esm/components/features.js.map +1 -1
  110. package/dist/esm/components/item.js +1 -1
  111. package/dist/esm/components/item.js.map +1 -1
  112. package/dist/esm/components/plan-button/index.js +5 -3
  113. package/dist/esm/components/plan-button/index.js.map +1 -1
  114. package/dist/esm/components/plan-button/style.scss +71 -47
  115. package/dist/esm/components/plan-div-td-container.js +4 -1
  116. package/dist/esm/components/plan-div-td-container.js.map +1 -1
  117. package/dist/esm/components/plan-logo.js +7 -4
  118. package/dist/esm/components/plan-logo.js.map +1 -1
  119. package/dist/esm/components/plan-type-selector/components/interval-type-dropdown.js +12 -1
  120. package/dist/esm/components/plan-type-selector/components/interval-type-dropdown.js.map +1 -1
  121. package/dist/esm/components/plan-type-selector/hooks/use-max-discount.js +3 -33
  122. package/dist/esm/components/plan-type-selector/hooks/use-max-discount.js.map +1 -1
  123. package/dist/esm/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.js +11 -13
  124. package/dist/esm/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.js.map +1 -1
  125. package/dist/esm/components/plans-2023-tooltip.js +16 -5
  126. package/dist/esm/components/plans-2023-tooltip.js.map +1 -1
  127. package/dist/esm/components/shared/action-button/index.js +22 -7
  128. package/dist/esm/components/shared/action-button/index.js.map +1 -1
  129. package/dist/esm/components/shared/action-button/style.scss +4 -0
  130. package/dist/esm/components/shared/billing-timeframe/index.js +8 -4
  131. package/dist/esm/components/shared/billing-timeframe/index.js.map +1 -1
  132. package/dist/esm/components/shared/header-price/index.js +60 -15
  133. package/dist/esm/components/shared/header-price/index.js.map +1 -1
  134. package/dist/esm/components/shared/header-price/style.scss +10 -2
  135. package/dist/esm/components/shared/storage/components/plan-storage.js +2 -2
  136. package/dist/esm/components/shared/storage/components/plan-storage.js.map +1 -1
  137. package/dist/esm/components/shared/storage/components/storage-dropdown.js +30 -7
  138. package/dist/esm/components/shared/storage/components/storage-dropdown.js.map +1 -1
  139. package/dist/esm/components/shared/storage/components/storage-feature-label.js +2 -1
  140. package/dist/esm/components/shared/storage/components/storage-feature-label.js.map +1 -1
  141. package/dist/esm/components/shared/storage/hooks/use-plan-storage.js +3 -1
  142. package/dist/esm/components/shared/storage/hooks/use-plan-storage.js.map +1 -1
  143. package/dist/esm/fixtures/sites-purchases.js +2 -4
  144. package/dist/esm/fixtures/sites-purchases.js.map +1 -1
  145. package/dist/esm/grid-context.js +4 -1
  146. package/dist/esm/grid-context.js.map +1 -1
  147. package/dist/esm/hooks/data-store/get-renewal-pricing-text.js +47 -0
  148. package/dist/esm/hooks/data-store/get-renewal-pricing-text.js.map +1 -0
  149. package/dist/esm/hooks/data-store/use-grid-plans-for-comparison-grid.js +6 -1
  150. package/dist/esm/hooks/data-store/use-grid-plans-for-comparison-grid.js.map +1 -1
  151. package/dist/esm/hooks/data-store/use-grid-plans-for-features-grid.js +6 -1
  152. package/dist/esm/hooks/data-store/use-grid-plans-for-features-grid.js.map +1 -1
  153. package/dist/esm/hooks/data-store/use-grid-plans.js +176 -22
  154. package/dist/esm/hooks/data-store/use-grid-plans.js.map +1 -1
  155. package/dist/esm/hooks/data-store/use-highlight-labels.js +14 -5
  156. package/dist/esm/hooks/data-store/use-highlight-labels.js.map +1 -1
  157. package/dist/esm/hooks/data-store/use-plan-billing-description.js +66 -11
  158. package/dist/esm/hooks/data-store/use-plan-billing-description.js.map +1 -1
  159. package/dist/esm/hooks/data-store/use-plan-billing-period.js +12 -0
  160. package/dist/esm/hooks/data-store/use-plan-billing-period.js.map +1 -0
  161. package/dist/esm/hooks/data-store/use-plan-features-for-grid-plans.js +77 -3
  162. package/dist/esm/hooks/data-store/use-plan-features-for-grid-plans.js.map +1 -1
  163. package/dist/esm/hooks/data-store/use-restructured-plan-features-for-comparison-grid.js +59 -11
  164. package/dist/esm/hooks/data-store/use-restructured-plan-features-for-comparison-grid.js.map +1 -1
  165. package/dist/esm/hooks/data-store/use-title-badges.js +17 -0
  166. package/dist/esm/hooks/data-store/use-title-badges.js.map +1 -0
  167. package/dist/esm/hooks/use-grid-size.js.map +1 -1
  168. package/dist/esm/hooks/use-is-large-currency.js +1 -1
  169. package/dist/esm/hooks/use-is-large-currency.js.map +1 -1
  170. package/dist/esm/hooks/use-visible-grid-plans.js +66 -0
  171. package/dist/esm/hooks/use-visible-grid-plans.js.map +1 -0
  172. package/dist/esm/index.js +4 -1
  173. package/dist/esm/index.js.map +1 -1
  174. package/dist/esm/lib/get-plan-features-object.js +15 -2
  175. package/dist/esm/lib/get-plan-features-object.js.map +1 -1
  176. package/dist/esm/lib/plan-pricing-utils.js +129 -0
  177. package/dist/esm/lib/plan-pricing-utils.js.map +1 -0
  178. package/dist/tsconfig-cjs.tsbuildinfo +1 -1
  179. package/dist/tsconfig.tsbuildinfo +1 -1
  180. package/dist/types/components/comparison-grid/index.d.ts +1 -1
  181. package/dist/types/components/comparison-grid/index.d.ts.map +1 -1
  182. package/dist/types/components/comparison-grid/index.stories.d.ts +2 -2
  183. package/dist/types/components/dropdown-option.d.ts.map +1 -1
  184. package/dist/types/components/features-grid/billing-timeframes.d.ts.map +1 -1
  185. package/dist/types/components/features-grid/client-logo-list/client-list.d.ts.map +1 -1
  186. package/dist/types/components/features-grid/client-logo-list/index.d.ts.map +1 -1
  187. package/dist/types/components/features-grid/enterprise-features.d.ts.map +1 -1
  188. package/dist/types/components/features-grid/index.d.ts.map +1 -1
  189. package/dist/types/components/features-grid/plan-features-list.d.ts.map +1 -1
  190. package/dist/types/components/features-grid/plan-headers.d.ts +2 -0
  191. package/dist/types/components/features-grid/plan-headers.d.ts.map +1 -1
  192. package/dist/types/components/features-grid/plan-logos.d.ts.map +1 -1
  193. package/dist/types/components/features-grid/plan-prices.d.ts.map +1 -1
  194. package/dist/types/components/features-grid/plan-tagline.d.ts.map +1 -1
  195. package/dist/types/components/features-grid/previous-features-included-title.d.ts.map +1 -1
  196. package/dist/types/components/features-grid/spotlight-plan.d.ts.map +1 -1
  197. package/dist/types/components/features-grid/table.d.ts.map +1 -1
  198. package/dist/types/components/features-grid/top-buttons.d.ts.map +1 -1
  199. package/dist/types/components/features.d.ts.map +1 -1
  200. package/dist/types/components/item.d.ts +2 -1
  201. package/dist/types/components/item.d.ts.map +1 -1
  202. package/dist/types/components/plan-button/index.d.ts +2 -1
  203. package/dist/types/components/plan-button/index.d.ts.map +1 -1
  204. package/dist/types/components/plan-div-td-container.d.ts +2 -0
  205. package/dist/types/components/plan-div-td-container.d.ts.map +1 -1
  206. package/dist/types/components/plan-logo.d.ts.map +1 -1
  207. package/dist/types/components/plan-type-selector/components/interval-type-dropdown.d.ts.map +1 -1
  208. package/dist/types/components/plan-type-selector/hooks/use-max-discount.d.ts.map +1 -1
  209. package/dist/types/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.d.ts.map +1 -1
  210. package/dist/types/components/plans-2023-tooltip.d.ts.map +1 -1
  211. package/dist/types/components/shared/action-button/index.d.ts +2 -1
  212. package/dist/types/components/shared/action-button/index.d.ts.map +1 -1
  213. package/dist/types/components/shared/billing-timeframe/index.d.ts.map +1 -1
  214. package/dist/types/components/shared/header-price/header-price-context.d.ts.map +1 -1
  215. package/dist/types/components/shared/header-price/index.d.ts.map +1 -1
  216. package/dist/types/components/shared/storage/components/plan-storage.d.ts.map +1 -1
  217. package/dist/types/components/shared/storage/components/storage-dropdown.d.ts.map +1 -1
  218. package/dist/types/components/shared/storage/components/storage-feature-label.d.ts.map +1 -1
  219. package/dist/types/components/shared/storage/hooks/use-plan-storage.d.ts +1 -1
  220. package/dist/types/components/shared/storage/hooks/use-plan-storage.d.ts.map +1 -1
  221. package/dist/types/css-mixins.d.ts.map +1 -1
  222. package/dist/types/fixtures/sites-purchases.d.ts +2 -4
  223. package/dist/types/fixtures/sites-purchases.d.ts.map +1 -1
  224. package/dist/types/grid-context.d.ts +4 -1
  225. package/dist/types/grid-context.d.ts.map +1 -1
  226. package/dist/types/hooks/data-store/get-renewal-pricing-text.d.ts +14 -0
  227. package/dist/types/hooks/data-store/get-renewal-pricing-text.d.ts.map +1 -0
  228. package/dist/types/hooks/data-store/types.d.ts +21 -0
  229. package/dist/types/hooks/data-store/types.d.ts.map +1 -1
  230. package/dist/types/hooks/data-store/use-grid-plan-for-spotlight.d.ts.map +1 -1
  231. package/dist/types/hooks/data-store/use-grid-plans-for-comparison-grid.d.ts +1 -1
  232. package/dist/types/hooks/data-store/use-grid-plans-for-comparison-grid.d.ts.map +1 -1
  233. package/dist/types/hooks/data-store/use-grid-plans-for-features-grid.d.ts +1 -1
  234. package/dist/types/hooks/data-store/use-grid-plans-for-features-grid.d.ts.map +1 -1
  235. package/dist/types/hooks/data-store/use-grid-plans.d.ts.map +1 -1
  236. package/dist/types/hooks/data-store/use-highlight-labels.d.ts.map +1 -1
  237. package/dist/types/hooks/data-store/use-plan-billing-description.d.ts.map +1 -1
  238. package/dist/types/hooks/data-store/use-plan-billing-period.d.ts +8 -0
  239. package/dist/types/hooks/data-store/use-plan-billing-period.d.ts.map +1 -0
  240. package/dist/types/hooks/data-store/use-plan-features-for-grid-plans.d.ts +4 -1
  241. package/dist/types/hooks/data-store/use-plan-features-for-grid-plans.d.ts.map +1 -1
  242. package/dist/types/hooks/data-store/use-plans-from-types.d.ts.map +1 -1
  243. package/dist/types/hooks/data-store/use-restructured-plan-features-for-comparison-grid.d.ts +4 -1
  244. package/dist/types/hooks/data-store/use-restructured-plan-features-for-comparison-grid.d.ts.map +1 -1
  245. package/dist/types/hooks/data-store/use-title-badges.d.ts +9 -0
  246. package/dist/types/hooks/data-store/use-title-badges.d.ts.map +1 -0
  247. package/dist/types/hooks/use-grid-size.d.ts +3 -2
  248. package/dist/types/hooks/use-grid-size.d.ts.map +1 -1
  249. package/dist/types/hooks/use-highlight-adjacency-matrix.d.ts.map +1 -1
  250. package/dist/types/hooks/use-visible-grid-plans.d.ts +14 -0
  251. package/dist/types/hooks/use-visible-grid-plans.d.ts.map +1 -0
  252. package/dist/types/index.d.ts +9 -1
  253. package/dist/types/index.d.ts.map +1 -1
  254. package/dist/types/lib/filter-unused-features-object.d.ts.map +1 -1
  255. package/dist/types/lib/get-plan-features-object.d.ts +1 -1
  256. package/dist/types/lib/get-plan-features-object.d.ts.map +1 -1
  257. package/dist/types/lib/plan-pricing-utils.d.ts +105 -0
  258. package/dist/types/lib/plan-pricing-utils.d.ts.map +1 -0
  259. package/dist/types/types.d.ts +33 -6
  260. package/dist/types/types.d.ts.map +1 -1
  261. package/package.json +39 -28
  262. package/src/_shared.scss +4 -3
  263. package/src/components/comparison-grid/index.stories.tsx +1 -1
  264. package/src/components/comparison-grid/index.tsx +263 -158
  265. package/src/components/comparison-grid/style.scss +10 -2
  266. package/src/components/features-grid/client-logo-list/client-list.tsx +0 -25
  267. package/src/components/features-grid/index.tsx +37 -19
  268. package/src/components/features-grid/plan-features-list.tsx +15 -4
  269. package/src/components/features-grid/plan-headers.tsx +10 -3
  270. package/src/components/features-grid/plan-tagline.tsx +1 -1
  271. package/src/components/features-grid/style.scss +111 -21
  272. package/src/components/features-grid/table.tsx +4 -2
  273. package/src/components/features.tsx +66 -6
  274. package/src/components/item.tsx +6 -3
  275. package/src/components/plan-button/index.tsx +7 -1
  276. package/src/components/plan-button/style.scss +71 -47
  277. package/src/components/plan-div-td-container.tsx +6 -2
  278. package/src/components/plan-logo.tsx +16 -9
  279. package/src/components/plan-type-selector/components/interval-type-dropdown.tsx +14 -1
  280. package/src/components/plan-type-selector/hooks/use-max-discount.ts +8 -47
  281. package/src/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.ts +19 -17
  282. package/src/components/plans-2023-tooltip.tsx +17 -5
  283. package/src/components/shared/action-button/index.tsx +46 -5
  284. package/src/components/shared/action-button/style.scss +4 -0
  285. package/src/components/shared/billing-timeframe/index.tsx +12 -7
  286. package/src/components/shared/header-price/index.tsx +129 -27
  287. package/src/components/shared/header-price/style.scss +10 -2
  288. package/src/components/shared/storage/components/plan-storage.tsx +2 -2
  289. package/src/components/shared/storage/components/storage-dropdown.tsx +36 -15
  290. package/src/components/shared/storage/components/storage-feature-label.tsx +2 -1
  291. package/src/components/shared/storage/hooks/use-plan-storage.ts +3 -0
  292. package/src/components/test/actions-button.tsx +5 -0
  293. package/src/components/test/billing-timeframe.tsx +1 -1
  294. package/src/components/test/header-price.tsx +342 -4
  295. package/src/fixtures/sites-purchases.ts +2 -4
  296. package/src/grid-context.tsx +9 -0
  297. package/src/hooks/data-store/get-renewal-pricing-text.ts +73 -0
  298. package/src/hooks/data-store/types.ts +21 -0
  299. package/src/hooks/data-store/use-grid-plans-for-comparison-grid.ts +10 -0
  300. package/src/hooks/data-store/use-grid-plans-for-features-grid.ts +10 -0
  301. package/src/hooks/data-store/use-grid-plans.tsx +189 -23
  302. package/src/hooks/data-store/use-highlight-labels.ts +12 -3
  303. package/src/hooks/data-store/use-plan-billing-description.tsx +80 -15
  304. package/src/hooks/data-store/use-plan-billing-period.tsx +28 -0
  305. package/src/hooks/data-store/use-plan-features-for-grid-plans.ts +135 -1
  306. package/src/hooks/data-store/use-restructured-plan-features-for-comparison-grid.ts +93 -20
  307. package/src/hooks/data-store/use-title-badges.ts +31 -0
  308. package/src/hooks/test/use-visible-grid-plans.tsx +116 -0
  309. package/src/hooks/use-grid-size.ts +3 -2
  310. package/src/hooks/use-is-large-currency.ts +1 -1
  311. package/src/hooks/use-visible-grid-plans.tsx +102 -0
  312. package/src/index.tsx +20 -0
  313. package/src/lib/get-plan-features-object.ts +23 -2
  314. package/src/lib/plan-pricing-utils.ts +211 -0
  315. package/src/lib/test/plan-pricing-utils.ts +594 -0
  316. package/src/style-imports.d.ts +3 -0
  317. package/src/types.ts +45 -4
  318. package/dist/cjs/components/features-grid/mobile-free-domain.js +0 -25
  319. package/dist/cjs/components/features-grid/mobile-free-domain.js.map +0 -1
  320. package/dist/cjs/lib/get-plan-pricing-info-from-grid-plans.js +0 -15
  321. package/dist/cjs/lib/get-plan-pricing-info-from-grid-plans.js.map +0 -1
  322. package/dist/cjs/lib/sort-plan-properties.js +0 -26
  323. package/dist/cjs/lib/sort-plan-properties.js.map +0 -1
  324. package/dist/esm/components/features-grid/mobile-free-domain.js +0 -23
  325. package/dist/esm/components/features-grid/mobile-free-domain.js.map +0 -1
  326. package/dist/esm/lib/get-plan-pricing-info-from-grid-plans.js +0 -12
  327. package/dist/esm/lib/get-plan-pricing-info-from-grid-plans.js.map +0 -1
  328. package/dist/esm/lib/sort-plan-properties.js +0 -23
  329. package/dist/esm/lib/sort-plan-properties.js.map +0 -1
  330. package/dist/types/components/features-grid/mobile-free-domain.d.ts +0 -8
  331. package/dist/types/components/features-grid/mobile-free-domain.d.ts.map +0 -1
  332. package/dist/types/lib/get-plan-pricing-info-from-grid-plans.d.ts +0 -9
  333. package/dist/types/lib/get-plan-pricing-info-from-grid-plans.d.ts.map +0 -1
  334. package/dist/types/lib/sort-plan-properties.d.ts +0 -3
  335. package/dist/types/lib/sort-plan-properties.d.ts.map +0 -1
  336. package/src/components/features-grid/mobile-free-domain.tsx +0 -51
  337. package/src/lib/get-plan-pricing-info-from-grid-plans.ts +0 -31
  338. package/src/lib/sort-plan-properties.ts +0 -27
  339. package/src/lib/test/sort-plan-properties.ts +0 -122
@@ -6,8 +6,10 @@ import {
6
6
  isWpcomEnterpriseGridPlan,
7
7
  } from '@automattic/calypso-products';
8
8
  import { AddOns, WpcomPlansUI } from '@automattic/data-stores';
9
+ import { formatCurrency } from '@automattic/number-formatters';
9
10
  import { useSelect } from '@wordpress/data';
10
- import { formatCurrency, useTranslate } from 'i18n-calypso';
11
+ import clsx from 'clsx';
12
+ import { useTranslate } from 'i18n-calypso';
11
13
  import { usePlansGridContext } from '../../../grid-context';
12
14
  import useIsLargeCurrency from '../../../hooks/use-is-large-currency';
13
15
  import { usePlanPricingInfoFromGridPlans } from '../../../hooks/use-plan-pricing-info-from-grid-plans';
@@ -28,6 +30,7 @@ type ActionButtonProps = {
28
30
  showMonthlyPrice: boolean;
29
31
  isStuck: boolean;
30
32
  visibleGridPlans: GridPlan[];
33
+ showPostButtonText?: boolean;
31
34
  };
32
35
 
33
36
  const ActionButton = ( {
@@ -38,12 +41,14 @@ const ActionButton = ( {
38
41
  isStuck,
39
42
  isInSignup,
40
43
  isMonthlyPlan,
44
+ showPostButtonText = true,
41
45
  }: ActionButtonProps ) => {
42
46
  const translate = useTranslate();
43
47
  const {
44
48
  gridPlansIndex,
45
49
  siteId,
46
50
  helpers: { useAction },
51
+ showBillingDescriptionForIncreasedRenewalPrice,
47
52
  } = usePlansGridContext();
48
53
  const {
49
54
  current,
@@ -80,7 +85,7 @@ const ActionButton = ( {
80
85
  );
81
86
 
82
87
  const {
83
- primary: { callback, text, status, variant },
88
+ primary: { callback, text, status, variant, ariaLabel },
84
89
  postButtonText,
85
90
  } = useAction( {
86
91
  availableForPurchase,
@@ -93,6 +98,8 @@ const ActionButton = ( {
93
98
  cartItemForPlan,
94
99
  currentPlanBillingPeriod,
95
100
  selectedStorageAddOn,
101
+ pricing: gridPlansIndex[ planSlug ]?.pricing,
102
+ isMonthlyPlan,
96
103
  } );
97
104
  const {
98
105
  primary: { callback: freeTrialCallback, text: freeTrialText },
@@ -108,6 +115,8 @@ const ActionButton = ( {
108
115
  cartItemForPlan: { product_slug: freeTrialPlanSlug ?? PLAN_FREE },
109
116
  currentPlanBillingPeriod,
110
117
  selectedStorageAddOn,
118
+ pricing: gridPlansIndex[ freeTrialPlanSlug ?? PLAN_FREE ]?.pricing,
119
+ isMonthlyPlan,
111
120
  } );
112
121
 
113
122
  const busy = status === 'blocked';
@@ -131,6 +140,7 @@ const ActionButton = ( {
131
140
  busy={ busy }
132
141
  disabled={ ! callback || 'disabled' === status }
133
142
  classes={ variant === 'secondary' ? 'is-secondary' : '' }
143
+ ariaLabel={ String( ariaLabel || '' ) }
134
144
  >
135
145
  { text }
136
146
  </PlanButton>
@@ -159,6 +169,7 @@ const ActionButton = ( {
159
169
  classes="is-storage-upgradeable"
160
170
  href={ storageAddOnCheckoutHref }
161
171
  busy={ busy }
172
+ ariaLabel={ translate( 'Upgrade storage for this plan' ).toString() }
162
173
  >
163
174
  { translate( 'Upgrade' ) }
164
175
  </PlanButton>
@@ -171,7 +182,13 @@ const ActionButton = ( {
171
182
  { freeTrialText }
172
183
  </PlanButton>
173
184
  { ! isStuck && ( // along side with the free trial CTA, we also provide an option for purchasing the plan directly here
174
- <PlanButton planSlug={ planSlug } onClick={ callback } busy={ busy } borderless>
185
+ <PlanButton
186
+ planSlug={ planSlug }
187
+ onClick={ callback }
188
+ busy={ busy }
189
+ borderless
190
+ ariaLabel={ String( ariaLabel || '' ) }
191
+ >
175
192
  { text }
176
193
  </PlanButton>
177
194
  ) }
@@ -184,15 +201,39 @@ const ActionButton = ( {
184
201
  busy={ busy }
185
202
  onClick={ callback }
186
203
  current={ current }
204
+ ariaLabel={ String( ariaLabel || '' ) }
187
205
  >
188
206
  { text }
189
207
  </PlanButton>
190
- { postButtonText && (
191
- <span className="plans-grid-next-action-button__label">{ postButtonText }</span>
208
+ { showPostButtonText && postButtonText && (
209
+ <span
210
+ className={ clsx( 'plans-grid-next-action-button__label', {
211
+ 'is-left-aligned': showBillingDescriptionForIncreasedRenewalPrice,
212
+ } ) }
213
+ >
214
+ { postButtonText }
215
+ </span>
192
216
  ) }
193
217
  </>
194
218
  );
195
219
  }
220
+ } else if (
221
+ showPostButtonText &&
222
+ postButtonText &&
223
+ showBillingDescriptionForIncreasedRenewalPrice
224
+ ) {
225
+ actionButton = (
226
+ <>
227
+ { actionButton }
228
+ <span
229
+ className={ clsx( 'plans-grid-next-action-button__label', {
230
+ 'is-left-aligned': showBillingDescriptionForIncreasedRenewalPrice,
231
+ } ) }
232
+ >
233
+ { postButtonText }
234
+ </span>
235
+ </>
236
+ );
196
237
  }
197
238
 
198
239
  return (
@@ -3,6 +3,10 @@
3
3
  font-size: 0.75rem;
4
4
  display: block;
5
5
  margin-top: 10px;
6
+
7
+ &.is-left-aligned {
8
+ text-align: left;
9
+ }
6
10
  }
7
11
 
8
12
  .plans-grid-next-action-button__multi {
@@ -4,8 +4,9 @@ import {
4
4
  isWooExpressPlan,
5
5
  isFreePlan,
6
6
  } from '@automattic/calypso-products';
7
+ import { formatCurrency } from '@automattic/number-formatters';
7
8
  import styled from '@emotion/styled';
8
- import { useTranslate, formatCurrency, fixMe } from 'i18n-calypso';
9
+ import { useTranslate, fixMe } from 'i18n-calypso';
9
10
  import { usePlansGridContext } from '../../../grid-context';
10
11
  import usePlanBillingDescription from '../../../hooks/data-store/use-plan-billing-description';
11
12
  import type { GridPlan } from '../../../types';
@@ -18,6 +19,10 @@ const DiscountPromotion = styled.div`
18
19
  margin-top: 6px;
19
20
  `;
20
21
 
22
+ const BillingTimeframeContainer = styled.p`
23
+ margin-bottom: 0;
24
+ `;
25
+
21
26
  interface RefundNoticeProps {
22
27
  showRefundPeriod?: boolean;
23
28
  planSlug: string;
@@ -70,10 +75,10 @@ const BillingTimeframe = ( { showRefundPeriod, planSlug }: Props ) => {
70
75
  ( ! introOffer || introOffer.isOfferComplete )
71
76
  ) {
72
77
  return (
73
- <div>
78
+ <BillingTimeframeContainer>
74
79
  <div>{ billingTimeframe }</div>
75
80
  <DiscountPromotion>{ planBillingDescription }</DiscountPromotion>
76
- </div>
81
+ </BillingTimeframeContainer>
77
82
  );
78
83
  }
79
84
 
@@ -81,7 +86,7 @@ const BillingTimeframe = ( { showRefundPeriod, planSlug }: Props ) => {
81
86
  const price = formatCurrency( 25000, 'USD', { stripZeros: true } );
82
87
 
83
88
  return (
84
- <div>
89
+ <BillingTimeframeContainer>
85
90
  { fixMe( {
86
91
  text: 'Starts at {{b}}%(price)s{{/b}} annually',
87
92
  newCopy: translate( 'Starts at {{b}}%(price)s{{/b}} annually', {
@@ -95,19 +100,19 @@ const BillingTimeframe = ( { showRefundPeriod, planSlug }: Props ) => {
95
100
  comment: 'Translators: the price is in US dollars for all users (US$25,000)',
96
101
  } ),
97
102
  } ) }
98
- </div>
103
+ </BillingTimeframeContainer>
99
104
  );
100
105
  }
101
106
 
102
107
  return (
103
- <div>
108
+ <BillingTimeframeContainer>
104
109
  { description }
105
110
  <RefundNotice
106
111
  showRefundPeriod={ showRefundPeriod }
107
112
  planSlug={ planSlug }
108
113
  billingPeriod={ billingPeriod }
109
114
  />
110
- </div>
115
+ </BillingTimeframeContainer>
111
116
  );
112
117
  };
113
118
 
@@ -13,6 +13,11 @@ import { useTranslate } from 'i18n-calypso';
13
13
  import { usePlansGridContext } from '../../../grid-context';
14
14
  import useIsLargeCurrency from '../../../hooks/use-is-large-currency';
15
15
  import { usePlanPricingInfoFromGridPlans } from '../../../hooks/use-plan-pricing-info-from-grid-plans';
16
+ import {
17
+ calculateDiscountPercentage,
18
+ fromPricingMetaForGridPlan,
19
+ getPlanPriceForDuration,
20
+ } from '../../../lib/plan-pricing-utils';
16
21
  import { useHeaderPriceContext } from './header-price-context';
17
22
  import type { GridPlan } from '../../../types';
18
23
  import './style.scss';
@@ -51,11 +56,18 @@ const HeaderPrice = ( { planSlug, visibleGridPlans }: HeaderPriceProps ) => {
51
56
  siteId,
52
57
  coupon,
53
58
  helpers,
59
+ showBillingDescriptionForIncreasedRenewalPrice,
60
+ isExperimentVariant,
54
61
  } = usePlansGridContext();
62
+
63
+ const pricingBadgeClassName = clsx( 'plans-grid-next-header-price__badge', {
64
+ 'is-plan-differentiators-experiment-badge': isExperimentVariant,
65
+ } );
55
66
  const { isAnyPlanPriceDiscounted, setIsAnyPlanPriceDiscounted } = useHeaderPriceContext();
56
67
  const {
57
68
  current,
58
69
  pricing: { currencyCode, originalPrice, discountedPrice, introOffer, billingPeriod },
70
+ isMonthlyPlan,
59
71
  } = gridPlansIndex[ planSlug ];
60
72
  const isPricedPlan = null !== originalPrice.monthly;
61
73
 
@@ -68,11 +80,12 @@ const HeaderPrice = ( { planSlug, visibleGridPlans }: HeaderPriceProps ) => {
68
80
  const isGridPlanOnIntroOffer = introOffer && ! introOffer.isOfferComplete;
69
81
 
70
82
  const { prices } = usePlanPricingInfoFromGridPlans( { gridPlans: visibleGridPlans } );
71
- const isLargeCurrency = useIsLargeCurrency( {
72
- prices,
73
- currencyCode: currencyCode || 'USD',
74
- ignoreWhitespace: true,
75
- } );
83
+ const isLargeCurrency =
84
+ useIsLargeCurrency( {
85
+ prices,
86
+ currencyCode: currencyCode || 'USD',
87
+ ignoreWhitespace: true,
88
+ } ) && ! showBillingDescriptionForIncreasedRenewalPrice; // a temporary fix to handle an issue with isLargeCurrency logic for intro offers
76
89
 
77
90
  const termVariantPlanSlug = useTermVariantPlanSlugForSavings( { planSlug, billingPeriod } );
78
91
  const termVariantPricing = Plans.usePricingMetaForGridPlans( {
@@ -83,19 +96,24 @@ const HeaderPrice = ( { planSlug, visibleGridPlans }: HeaderPriceProps ) => {
83
96
  useCheckPlanAvailabilityForPurchase: helpers?.useCheckPlanAvailabilityForPurchase,
84
97
  } )?.[ termVariantPlanSlug ?? '' ];
85
98
 
86
- const termVariantPrice =
87
- termVariantPricing?.discountedPrice.monthly ?? termVariantPricing?.originalPrice.monthly ?? 0;
88
- const planPrice = discountedPrice.monthly ?? originalPrice.monthly ?? 0;
89
- const savings =
90
- termVariantPrice > planPrice
91
- ? Math.floor( ( ( termVariantPrice - planPrice ) / termVariantPrice ) * 100 )
99
+ const termVariantInfo = termVariantPricing
100
+ ? fromPricingMetaForGridPlan( termVariantPricing )
101
+ : null;
102
+ const currentPlanInfo = fromPricingMetaForGridPlan( gridPlansIndex[ planSlug ].pricing );
103
+ let savings =
104
+ termVariantInfo && currentPlanInfo
105
+ ? calculateDiscountPercentage(
106
+ getPlanPriceForDuration( termVariantInfo, currentPlanInfo.termMonths ),
107
+ getPlanPriceForDuration( currentPlanInfo, currentPlanInfo.termMonths )
108
+ ) ?? 0
92
109
  : 0;
93
110
 
94
111
  useEffect( () => {
95
112
  if (
96
113
  isGridPlanOneTimeDiscounted ||
97
114
  isGridPlanOnIntroOffer ||
98
- ( enableTermSavingsPriceDisplay && savings )
115
+ ( enableTermSavingsPriceDisplay && savings ) ||
116
+ ( showBillingDescriptionForIncreasedRenewalPrice && !! termVariantPricing )
99
117
  ) {
100
118
  setIsAnyPlanPriceDiscounted( true );
101
119
  }
@@ -105,6 +123,8 @@ const HeaderPrice = ( { planSlug, visibleGridPlans }: HeaderPriceProps ) => {
105
123
  isGridPlanOneTimeDiscounted,
106
124
  savings,
107
125
  setIsAnyPlanPriceDiscounted,
126
+ showBillingDescriptionForIncreasedRenewalPrice,
127
+ termVariantPricing,
108
128
  ] );
109
129
 
110
130
  if ( isWpcomEnterpriseGridPlan( planSlug ) || ! isPricedPlan ) {
@@ -112,13 +132,41 @@ const HeaderPrice = ( { planSlug, visibleGridPlans }: HeaderPriceProps ) => {
112
132
  }
113
133
 
114
134
  if ( isGridPlanOnIntroOffer ) {
135
+ // Use the monthly plan price for renewal pricing, instead of the intro offer renewal price
136
+ const compareToMonthlyPrice =
137
+ ( showBillingDescriptionForIncreasedRenewalPrice && termVariantPricing
138
+ ? termVariantPricing.originalPrice.monthly
139
+ : originalPrice.monthly ) ?? 0;
140
+ const monthlyPrice =
141
+ typeof discountedPrice.monthly === 'number'
142
+ ? discountedPrice.monthly
143
+ : introOffer.rawPrice.monthly;
144
+ // Recalculate the savings for Monthly plans with introductory offers
145
+ // since we are comparing the introductory price with the same plan
146
+ // renewal price, instead of comparing yearly to monthly costs for
147
+ // the same period.
148
+ if (
149
+ showBillingDescriptionForIncreasedRenewalPrice &&
150
+ compareToMonthlyPrice > monthlyPrice &&
151
+ isMonthlyPlan
152
+ ) {
153
+ savings = calculateDiscountPercentage( compareToMonthlyPrice, monthlyPrice ) ?? 0;
154
+ }
115
155
  return (
116
156
  <div className="plans-grid-next-header-price">
117
157
  { ! current && (
118
- <div className="plans-grid-next-header-price__badge">
119
- { translate( 'Special Offer' ) }
158
+ <div className={ pricingBadgeClassName }>
159
+ { showBillingDescriptionForIncreasedRenewalPrice
160
+ ? translate( 'Save %(savings)d%%', {
161
+ args: { savings },
162
+ comment: 'Example: Save 35%',
163
+ } )
164
+ : translate( 'Special Offer' ) }
120
165
  </div>
121
166
  ) }
167
+ { current && visibleGridPlans.length > 1 && (
168
+ <div className={ clsx( pricingBadgeClassName, 'is-hidden' ) }>' '</div>
169
+ ) }
122
170
  <div
123
171
  className={ clsx( 'plans-grid-next-header-price__pricing-group', {
124
172
  'is-large-currency': isLargeCurrency,
@@ -126,22 +174,18 @@ const HeaderPrice = ( { planSlug, visibleGridPlans }: HeaderPriceProps ) => {
126
174
  >
127
175
  <PlanPrice
128
176
  currencyCode={ currencyCode }
129
- rawPrice={ originalPrice.monthly }
177
+ rawPrice={ compareToMonthlyPrice }
130
178
  displayPerMonthNotation={ false }
131
- isLargeCurrency
179
+ isLargeCurrency={ isLargeCurrency }
132
180
  isSmallestUnit
133
181
  priceDisplayWrapperClassName="plans-grid-next-header-price__display-wrapper"
134
182
  original
135
183
  />
136
184
  <PlanPrice
137
185
  currencyCode={ currencyCode }
138
- rawPrice={
139
- typeof discountedPrice.monthly === 'number'
140
- ? discountedPrice.monthly
141
- : introOffer.rawPrice.monthly
142
- }
186
+ rawPrice={ monthlyPrice }
143
187
  displayPerMonthNotation={ false }
144
- isLargeCurrency
188
+ isLargeCurrency={ isLargeCurrency }
145
189
  isSmallestUnit
146
190
  priceDisplayWrapperClassName="plans-grid-next-header-price__display-wrapper"
147
191
  discounted
@@ -151,12 +195,61 @@ const HeaderPrice = ( { planSlug, visibleGridPlans }: HeaderPriceProps ) => {
151
195
  );
152
196
  }
153
197
 
154
- if ( isGridPlanOneTimeDiscounted ) {
198
+ // Handle cases where a plan is ineligible for intro offer, but we still
199
+ // want to show the crossed-out monthly price.
200
+ if ( showBillingDescriptionForIncreasedRenewalPrice && termVariantPricing ) {
201
+ const compareToMonthlyPrice = termVariantPricing.originalPrice.monthly ?? 0;
202
+ const monthlyPrice =
203
+ typeof discountedPrice.monthly === 'number'
204
+ ? discountedPrice.monthly
205
+ : originalPrice.monthly ?? 0;
155
206
  return (
156
207
  <div className="plans-grid-next-header-price">
157
- <div className="plans-grid-next-header-price__badge">
158
- { translate( 'One time discount' ) }
208
+ { ! current && savings > 0 && (
209
+ <div className={ pricingBadgeClassName }>
210
+ { translate( 'Save %(savings)d%%', {
211
+ args: { savings },
212
+ comment: 'Example: Save 35%',
213
+ } ) }
214
+ </div>
215
+ ) }
216
+ { ( ( ! current && savings <= 0 ) || ( current && visibleGridPlans.length > 1 ) ) && (
217
+ <div className={ clsx( pricingBadgeClassName, 'is-hidden' ) }>' '</div>
218
+ ) }
219
+ <div
220
+ className={ clsx( 'plans-grid-next-header-price__pricing-group', {
221
+ 'is-large-currency': isLargeCurrency,
222
+ } ) }
223
+ >
224
+ { compareToMonthlyPrice > monthlyPrice && (
225
+ <PlanPrice
226
+ currencyCode={ currencyCode }
227
+ rawPrice={ compareToMonthlyPrice }
228
+ displayPerMonthNotation={ false }
229
+ isLargeCurrency={ isLargeCurrency }
230
+ isSmallestUnit
231
+ priceDisplayWrapperClassName="plans-grid-next-header-price__display-wrapper"
232
+ original
233
+ />
234
+ ) }
235
+ <PlanPrice
236
+ currencyCode={ currencyCode }
237
+ rawPrice={ monthlyPrice }
238
+ displayPerMonthNotation={ false }
239
+ isLargeCurrency={ isLargeCurrency }
240
+ isSmallestUnit
241
+ priceDisplayWrapperClassName="plans-grid-next-header-price__display-wrapper"
242
+ discounted
243
+ />
159
244
  </div>
245
+ </div>
246
+ );
247
+ }
248
+
249
+ if ( isGridPlanOneTimeDiscounted ) {
250
+ return (
251
+ <div className="plans-grid-next-header-price">
252
+ <div className={ pricingBadgeClassName }>{ translate( 'One time discount' ) }</div>
160
253
  <div
161
254
  className={ clsx( 'plans-grid-next-header-price__pricing-group', {
162
255
  'is-large-currency': isLargeCurrency,
@@ -188,7 +281,7 @@ const HeaderPrice = ( { planSlug, visibleGridPlans }: HeaderPriceProps ) => {
188
281
  if ( enableTermSavingsPriceDisplay && termVariantPricing && savings ) {
189
282
  return (
190
283
  <div className="plans-grid-next-header-price">
191
- <div className="plans-grid-next-header-price__badge">
284
+ <div className={ pricingBadgeClassName }>
192
285
  { translate( 'Save %(savings)d%%', {
193
286
  args: { savings },
194
287
  comment: 'Example: Save 35%',
@@ -225,7 +318,16 @@ const HeaderPrice = ( { planSlug, visibleGridPlans }: HeaderPriceProps ) => {
225
318
  if ( isAnyPlanPriceDiscounted ) {
226
319
  return (
227
320
  <div className="plans-grid-next-header-price">
228
- <div className="plans-grid-next-header-price__badge is-hidden">' '</div>
321
+ { showBillingDescriptionForIncreasedRenewalPrice && savings ? (
322
+ <div className={ pricingBadgeClassName }>
323
+ { translate( 'Save %(savings)d%%', {
324
+ args: { savings },
325
+ comment: 'Example: Save 35%',
326
+ } ) }
327
+ </div>
328
+ ) : (
329
+ <div className={ clsx( pricingBadgeClassName, 'is-hidden' ) }>' '</div>
330
+ ) }
229
331
  { isLargeCurrency ? (
230
332
  <div className="plans-grid-next-header-price__pricing-group is-large-currency">
231
333
  <PlanPrice
@@ -1,3 +1,5 @@
1
+ @import "@automattic/typography/styles/fonts";
2
+
1
3
  .plans-grid-next-header-price {
2
4
  padding: 0 20px;
3
5
  margin: 0 0 4px 0;
@@ -23,7 +25,7 @@
23
25
 
24
26
  .plan-price__currency-symbol,
25
27
  .plan-price__tax-amount {
26
- color: var(--color-text-subtle);
28
+ color: var(--studio-gray-20);
27
29
  }
28
30
 
29
31
  .plan-price__integer {
@@ -93,6 +95,12 @@
93
95
  white-space: nowrap;
94
96
  width: fit-content;
95
97
 
98
+ // Same colors as Badge type="info-green" (@automattic/components); only for pricing differentiators experiment (non-control).
99
+ &.is-plan-differentiators-experiment-badge {
100
+ background-color: rgba(184, 230, 191, 0.64);
101
+ color: var(--studio-green-80);
102
+ }
103
+
96
104
  &.is-hidden {
97
105
  visibility: hidden;
98
106
  }
@@ -102,7 +110,7 @@
102
110
  justify-content: flex-end;
103
111
  display: flex;
104
112
  flex-direction: row-reverse;
105
- align-items: flex-end;
113
+ align-items: last baseline;
106
114
  gap: 4px;
107
115
 
108
116
  &.is-large-currency {
@@ -21,7 +21,7 @@ const PlanStorage = ( {
21
21
  showUpgradeableStorage,
22
22
  }: Props ) => {
23
23
  const { siteId, gridPlansIndex } = usePlansGridContext();
24
- const { availableForPurchase, current } = gridPlansIndex[ planSlug ];
24
+ const { availableForPurchase, current, planTitle } = gridPlansIndex[ planSlug ];
25
25
  const availableStorageAddOns = AddOns.useAvailableStorageAddOns( { siteId } );
26
26
 
27
27
  if ( ! options?.isTableCell && isWpcomEnterpriseGridPlan( planSlug ) ) {
@@ -38,7 +38,7 @@ const PlanStorage = ( {
38
38
  ELIGIBLE_PLANS_FOR_STORAGE_UPGRADE.includes( planSlug );
39
39
 
40
40
  return (
41
- <div className="plans-grid-next-plan-storage">
41
+ <div className="plans-grid-next-plan-storage" data-plan-title={ planTitle }>
42
42
  { canUpgradeStorageForPlan ? (
43
43
  <StorageDropdown planSlug={ planSlug } onStorageAddOnClick={ onStorageAddOnClick } />
44
44
  ) : (
@@ -1,3 +1,4 @@
1
+ /* eslint-disable jsx-a11y/tabindex-no-positive */
1
2
  import { type AddOnMeta, AddOns, WpcomPlansUI } from '@automattic/data-stores';
2
3
  import { CustomSelectControl } from '@wordpress/components';
3
4
  import { useDispatch, useSelect } from '@wordpress/data';
@@ -40,14 +41,19 @@ const StorageDropdownOption = ( {
40
41
  const addOnStorageString = useStorageString( addOnStorage || 0 );
41
42
 
42
43
  const title = addOnStorage
43
- ? translate( '%(planStorageString)s + %(addOnStorageString)s', {
44
+ ? translate( '%(planStorageString)s + %(addOnStorageString)s storage', {
44
45
  args: {
45
46
  planStorageString,
46
47
  addOnStorageString,
47
48
  },
49
+ comment: 'Storage option with add-on. Example: "50GB + 100GB storage"',
48
50
  } )
49
- : planStorageString;
51
+ : translate( '%(planStorageString)s storage', {
52
+ args: { planStorageString },
53
+ comment: 'Storage option. Example: "50GB storage"',
54
+ } );
50
55
 
56
+ // Only show price for add-on options, not for the base plan storage
51
57
  const priceString =
52
58
  price && addOnStorage
53
59
  ? translate( '%(price)s/month, billed yearly', {
@@ -55,13 +61,13 @@ const StorageDropdownOption = ( {
55
61
  comment:
56
62
  'The cost of a storage add on per month. Example reads as "$50/month, billed yearly"',
57
63
  } )
58
- : translate( 'Included in plan' );
64
+ : null;
59
65
 
60
66
  return priceOnSeparateLine ? (
61
67
  <span className="plans-grid-next-storage-dropdown__option-title">{ title }</span>
62
68
  ) : (
63
69
  <DropdownOption className="plans-grid-next-storage-dropdown__option" title={ title }>
64
- <div>{ priceString }</div>
70
+ { priceString && <div>{ priceString }</div> }
65
71
  </DropdownOption>
66
72
  );
67
73
  };
@@ -137,18 +143,30 @@ const StorageDropdown = ( { planSlug, onStorageAddOnClick }: StorageDropdownProp
137
143
  );
138
144
  const selectedStorageAddOnStorage = selectedStorageAddOn?.quantity ?? 0;
139
145
 
146
+ const planStorageString = useStorageString( planStorage );
147
+ const selectedAddOnStorageString = useStorageString( selectedStorageAddOnStorage );
148
+ const accessibleOptionName = selectedStorageAddOnStorage
149
+ ? translate( '%(planStorageString)s + %(addOnStorageString)s storage', {
150
+ args: {
151
+ planStorageString,
152
+ addOnStorageString: selectedAddOnStorageString,
153
+ },
154
+ comment: 'Storage amount display with add-on. Example: "50GB + 100GB storage"',
155
+ } )
156
+ : translate( '%(planStorageString)s storage', {
157
+ args: { planStorageString },
158
+ comment: 'Storage amount display. Example: "50GB storage"',
159
+ } );
160
+
140
161
  const selectedOption = {
141
162
  key: selectedStorageOptionForPlan,
142
- name: (
143
- <StorageDropdownOption
144
- price={ selectedStorageAddOn?.prices?.formattedMonthlyPrice }
145
- planStorage={ planStorage }
146
- addOnStorage={ selectedStorageAddOnStorage }
147
- priceOnSeparateLine
148
- />
149
- ) as unknown as string,
163
+ name: accessibleOptionName as string,
150
164
  };
151
165
 
166
+ const accessibleDescription = translate( 'Currently selected storage option: %(storageOption)s', {
167
+ args: { storageOption: accessibleOptionName },
168
+ } );
169
+
152
170
  const handleOnChange = useCallback(
153
171
  ( { selectedItem }: { selectedItem: { key: string } } ) => {
154
172
  const addOnSlug = selectedItem?.key as AddOns.StorageAddOnSlug;
@@ -162,15 +180,18 @@ const StorageDropdown = ( { planSlug, onStorageAddOnClick }: StorageDropdownProp
162
180
  );
163
181
 
164
182
  return (
165
- <>
183
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
184
+ <div tabIndex={ 1 }>
166
185
  <CustomSelectControl
167
186
  __next40pxDefaultSize
168
187
  hideLabelFromVision
169
188
  options={ selectControlOptions || [] }
170
189
  value={ selectedOption }
171
190
  onChange={ handleOnChange }
172
- label=""
191
+ label={ translate( 'Storage options for this plan' ) }
192
+ describedBy={ accessibleDescription as string }
173
193
  />
194
+
174
195
  { selectedStorageAddOn?.prices?.formattedMonthlyPrice && (
175
196
  <div className="plans-grid-next-storage-dropdown__addon-offset-price-container">
176
197
  <span className="plans-grid-next-storage-dropdown__addon-offset-price">
@@ -180,7 +201,7 @@ const StorageDropdown = ( { planSlug, onStorageAddOnClick }: StorageDropdownProp
180
201
  </span>
181
202
  </div>
182
203
  ) }
183
- </>
204
+ </div>
184
205
  );
185
206
  };
186
207
 
@@ -1,6 +1,7 @@
1
1
  import { PlanSlug } from '@automattic/calypso-products';
2
+ import { formatCurrency } from '@automattic/number-formatters';
2
3
  import clsx from 'clsx';
3
- import { formatCurrency, useTranslate } from 'i18n-calypso';
4
+ import { useTranslate } from 'i18n-calypso';
4
5
  import { usePlansGridContext } from '../../../../grid-context';
5
6
  import useIsLargeCurrency from '../../../../hooks/use-is-large-currency';
6
7
  import usePlanStorage from '../hooks/use-plan-storage';
@@ -3,6 +3,7 @@ import {
3
3
  FEATURE_6GB_STORAGE,
4
4
  FEATURE_13GB_STORAGE,
5
5
  FEATURE_50GB_STORAGE,
6
+ FEATURE_100GB_STORAGE,
6
7
  FEATURE_200GB_STORAGE,
7
8
  FEATURE_P2_3GB_STORAGE,
8
9
  FEATURE_P2_13GB_STORAGE,
@@ -27,6 +28,8 @@ function usePlanStorage( planSlug: PlanSlug ) {
27
28
  return 13;
28
29
  case FEATURE_50GB_STORAGE:
29
30
  return 50;
31
+ case FEATURE_100GB_STORAGE:
32
+ return 100;
30
33
  case FEATURE_P2_3GB_STORAGE:
31
34
  return 3;
32
35
  case FEATURE_P2_13GB_STORAGE:
@@ -10,6 +10,7 @@ jest.mock( '@wordpress/data', () => ( {
10
10
  createReduxStore: jest.fn(),
11
11
  createSelector: jest.fn(),
12
12
  register: jest.fn(),
13
+ registerStore: jest.fn(),
13
14
  useDispatch: jest.fn(),
14
15
  } ) );
15
16
  jest.mock( '@wordpress/element', () => ( {
@@ -26,6 +27,10 @@ jest.mock( '@automattic/data-stores', () => ( {
26
27
  Purchases: {
27
28
  useSitePurchasesByProductSlug: jest.fn(),
28
29
  },
30
+ Plans: {
31
+ ...jest.requireActual( '@automattic/data-stores' ).Plans,
32
+ usePricingMetaForGridPlans: jest.fn(),
33
+ },
29
34
  } ) );
30
35
  jest.mock( 'i18n-calypso', () => ( {
31
36
  ...jest.requireActual( 'i18n-calypso' ),
@@ -34,8 +34,8 @@ import {
34
34
  PLAN_TRIENNIAL_PERIOD,
35
35
  } from '@automattic/calypso-products';
36
36
  import { Plans } from '@automattic/data-stores';
37
+ import { formatCurrency } from '@automattic/number-formatters';
37
38
  import { render } from '@testing-library/react';
38
- import { formatCurrency } from 'i18n-calypso';
39
39
  import React from 'react';
40
40
  import { usePlansGridContext } from '../../grid-context';
41
41
  import BillingTimeframe from '../shared/billing-timeframe';