@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
@@ -1,9 +1,22 @@
1
1
  import {
2
+ FEATURE_AI_ASSISTANT,
3
+ FEATURE_AI_WEBSITE_BUILDER,
4
+ FEATURE_AI_WRITER_DESIGNER,
5
+ FEATURE_BUILT_IN_SITE_ASSISTANT,
2
6
  FEATURE_CUSTOM_DOMAIN,
7
+ FEATURE_EMAIL_MARKETING,
8
+ FEATURE_ENHANCED_AI_ASSISTANT_AND_TOOLS,
9
+ FEATURE_GUIDED_WEBSITE_BUILDER,
10
+ FEATURE_SIMPLE_PAYMENTS,
11
+ FEATURE_PROFESSIONAL_EMAIL_FREE_YEAR,
3
12
  applyTestFiltersToPlansList,
13
+ isBusinessPlan,
4
14
  isMonthly,
15
+ isPersonalPlan,
16
+ isPremiumPlan,
5
17
  } from '@automattic/calypso-products';
6
18
  import { useMemo } from '@wordpress/element';
19
+ import { useTranslate } from 'i18n-calypso';
7
20
  import getPlanFeaturesObject from '../../lib/get-plan-features-object';
8
21
  import useHighlightedFeatures from './use-highlighted-features';
9
22
  import type {
@@ -13,6 +26,61 @@ import type {
13
26
  GridPlan,
14
27
  } from '../../types';
15
28
  import type { FeatureObject, FeatureList } from '@automattic/calypso-products';
29
+ import type { TranslateResult } from 'i18n-calypso';
30
+
31
+ function isPremiumWebsiteBuilderPillFeature( featureSlug: string ): boolean {
32
+ return (
33
+ featureSlug === FEATURE_AI_WEBSITE_BUILDER ||
34
+ featureSlug === FEATURE_GUIDED_WEBSITE_BUILDER ||
35
+ featureSlug === FEATURE_AI_WRITER_DESIGNER
36
+ );
37
+ }
38
+
39
+ function isBusinessAssistantPillFeature( featureSlug: string ): boolean {
40
+ return (
41
+ featureSlug === FEATURE_ENHANCED_AI_ASSISTANT_AND_TOOLS ||
42
+ featureSlug === FEATURE_AI_ASSISTANT ||
43
+ featureSlug === FEATURE_BUILT_IN_SITE_ASSISTANT ||
44
+ featureSlug === FEATURE_AI_WRITER_DESIGNER
45
+ );
46
+ }
47
+
48
+ function getPricingDifferentiationFeatureBadgeText(
49
+ planSlug: string,
50
+ featureSlug: string,
51
+ translate: ( text: string ) => TranslateResult,
52
+ options?: { suppressAiPills?: boolean }
53
+ ): TranslateResult | undefined {
54
+ const suppressAiPills = options?.suppressAiPills ?? false;
55
+
56
+ if ( isPersonalPlan( planSlug ) && featureSlug === FEATURE_CUSTOM_DOMAIN ) {
57
+ return translate( 'Free' );
58
+ }
59
+ if (
60
+ ! suppressAiPills &&
61
+ isPremiumPlan( planSlug ) &&
62
+ isPremiumWebsiteBuilderPillFeature( featureSlug )
63
+ ) {
64
+ return translate( 'AI' );
65
+ }
66
+ if ( isPremiumPlan( planSlug ) && featureSlug === FEATURE_SIMPLE_PAYMENTS ) {
67
+ return translate( 'New' );
68
+ }
69
+ if (
70
+ ! suppressAiPills &&
71
+ isBusinessPlan( planSlug ) &&
72
+ isBusinessAssistantPillFeature( featureSlug )
73
+ ) {
74
+ return translate( 'AI' );
75
+ }
76
+ if ( isBusinessPlan( planSlug ) && featureSlug === FEATURE_PROFESSIONAL_EMAIL_FREE_YEAR ) {
77
+ return translate( 'Email' );
78
+ }
79
+ if ( isBusinessPlan( planSlug ) && featureSlug === FEATURE_EMAIL_MARKETING ) {
80
+ return translate( 'New' );
81
+ }
82
+ return undefined;
83
+ }
16
84
 
17
85
  export type UsePlanFeaturesForGridPlans = ( {
18
86
  gridPlans,
@@ -23,6 +91,9 @@ export type UsePlanFeaturesForGridPlans = ( {
23
91
  showLegacyStorageFeature,
24
92
  selectedFeature,
25
93
  isInSignup,
94
+ useVar42NoAiFeatures,
95
+ showPricingDifferentiationFeaturePills,
96
+ isExperimentVariant,
26
97
  }: {
27
98
  gridPlans: Omit< GridPlan, 'features' >[];
28
99
  allFeaturesList: FeatureList;
@@ -31,6 +102,9 @@ export type UsePlanFeaturesForGridPlans = ( {
31
102
  selectedFeature?: string | null;
32
103
  showLegacyStorageFeature?: boolean;
33
104
  isInSignup?: boolean;
105
+ useVar42NoAiFeatures?: boolean;
106
+ showPricingDifferentiationFeaturePills?: boolean;
107
+ isExperimentVariant?: boolean;
34
108
  } ) => { [ planSlug: string ]: PlanFeaturesForGridPlan };
35
109
 
36
110
  /**
@@ -46,7 +120,11 @@ const usePlanFeaturesForGridPlans: UsePlanFeaturesForGridPlans = ( {
46
120
  selectedFeature,
47
121
  showLegacyStorageFeature,
48
122
  isInSignup,
123
+ useVar42NoAiFeatures,
124
+ showPricingDifferentiationFeaturePills,
125
+ isExperimentVariant,
49
126
  } ) => {
127
+ const translate = useTranslate();
50
128
  const highlightedFeatures = useHighlightedFeatures( { intent: intent ?? null, isInSignup } );
51
129
  return useMemo( () => {
52
130
  return gridPlans.reduce(
@@ -58,7 +136,21 @@ const usePlanFeaturesForGridPlans: UsePlanFeaturesForGridPlans = ( {
58
136
  let wpcomFeatures: FeatureObject[] = [];
59
137
  let jetpackFeatures: FeatureObject[] = [];
60
138
 
61
- if ( 'plans-newsletter' === intent ) {
139
+ if ( useVar42NoAiFeatures ) {
140
+ wpcomFeatures = getPlanFeaturesObject(
141
+ allFeaturesList,
142
+ planConstantObj?.getVar42NoAiSignupWpcomFeatures?.() ??
143
+ planConstantObj?.get2023PricingGridSignupWpcomFeatures?.() ??
144
+ [],
145
+ isExperimentVariant ?? true
146
+ );
147
+
148
+ jetpackFeatures = getPlanFeaturesObject(
149
+ allFeaturesList,
150
+ planConstantObj.get2023PricingGridSignupJetpackFeatures?.() ?? [],
151
+ isExperimentVariant ?? true
152
+ );
153
+ } else if ( 'plans-newsletter' === intent ) {
62
154
  wpcomFeatures = getPlanFeaturesObject(
63
155
  allFeaturesList,
64
156
  planConstantObj?.getNewsletterSignupFeatures?.() ?? []
@@ -103,6 +195,30 @@ const usePlanFeaturesForGridPlans: UsePlanFeaturesForGridPlans = ( {
103
195
  return true;
104
196
  } );
105
197
  }
198
+ } else if ( 'plans-wordpress-hosting' === intent ) {
199
+ // Use visual split features for WordPress hosting intent
200
+ if ( planConstantObj?.getVisualSplitBusinessFeatures ) {
201
+ wpcomFeatures = getPlanFeaturesObject(
202
+ allFeaturesList,
203
+ planConstantObj.getVisualSplitBusinessFeatures() ?? []
204
+ );
205
+ } else if ( planConstantObj?.getVisualSplitCommerceFeatures ) {
206
+ wpcomFeatures = getPlanFeaturesObject(
207
+ allFeaturesList,
208
+ planConstantObj.getVisualSplitCommerceFeatures() ?? []
209
+ );
210
+ } else {
211
+ // Fallback to default features if visual split features aren't available
212
+ wpcomFeatures = getPlanFeaturesObject(
213
+ allFeaturesList,
214
+ planConstantObj?.get2023PricingGridSignupWpcomFeatures?.() ?? []
215
+ );
216
+ }
217
+
218
+ jetpackFeatures = getPlanFeaturesObject(
219
+ allFeaturesList,
220
+ planConstantObj.get2023PricingGridSignupJetpackFeatures?.() ?? []
221
+ );
106
222
  } else {
107
223
  wpcomFeatures = getPlanFeaturesObject(
108
224
  allFeaturesList,
@@ -179,12 +295,26 @@ const usePlanFeaturesForGridPlans: UsePlanFeaturesForGridPlans = ( {
179
295
  feature.getSlug()
180
296
  );
181
297
 
298
+ const featureSlug = feature.getSlug();
299
+
300
+ const badgeText = showPricingDifferentiationFeaturePills
301
+ ? getPricingDifferentiationFeatureBadgeText( planSlug, featureSlug, translate, {
302
+ suppressAiPills: useVar42NoAiFeatures,
303
+ } )
304
+ : undefined;
305
+
182
306
  wpcomFeaturesTransformed.push( {
183
307
  ...feature,
184
308
  availableOnlyForAnnualPlans,
185
309
  availableForCurrentPlan: ! isMonthlyPlan || ! availableOnlyForAnnualPlans,
310
+ ...( badgeText && { badgeText } ),
186
311
  } );
187
312
  } );
313
+
314
+ if ( wpcomFeaturesTransformed.length > 0 && isExperimentVariant ) {
315
+ const lastIndex = wpcomFeaturesTransformed.length - 1;
316
+ wpcomFeaturesTransformed[ lastIndex ].isExperimentLastFeature = true;
317
+ }
188
318
  }
189
319
 
190
320
  const storageFeature = planConstantObj.getStorageFeature?.(
@@ -213,6 +343,10 @@ const usePlanFeaturesForGridPlans: UsePlanFeaturesForGridPlans = ( {
213
343
  showLegacyStorageFeature,
214
344
  allFeaturesList,
215
345
  hasRedeemedDomainCredit,
346
+ useVar42NoAiFeatures,
347
+ showPricingDifferentiationFeaturePills,
348
+ isExperimentVariant,
349
+ translate,
216
350
  ] );
217
351
  };
218
352
 
@@ -5,7 +5,7 @@ import {
5
5
  type FeatureList,
6
6
  FEATURE_CUSTOM_DOMAIN,
7
7
  } from '@automattic/calypso-products';
8
- import { useMemo } from 'react';
8
+ import { useMemo } from '@wordpress/element';
9
9
  import getPlanFeaturesObject from '../../lib/get-plan-features-object';
10
10
  import usePlanFeaturesForGridPlans from './use-plan-features-for-grid-plans';
11
11
  import type {
@@ -22,6 +22,9 @@ export type UseRestructuredPlanFeaturesForComparisonGrid = ( {
22
22
  intent,
23
23
  showLegacyStorageFeature,
24
24
  selectedFeature,
25
+ useVar42NoAiFeatures,
26
+ showPricingDifferentiationFeaturePills,
27
+ isExperimentVariant,
25
28
  }: {
26
29
  gridPlans: Omit< GridPlan, 'features' >[];
27
30
  allFeaturesList: FeatureList;
@@ -29,6 +32,9 @@ export type UseRestructuredPlanFeaturesForComparisonGrid = ( {
29
32
  intent?: PlansIntent;
30
33
  selectedFeature?: string | null;
31
34
  showLegacyStorageFeature?: boolean;
35
+ useVar42NoAiFeatures?: boolean;
36
+ showPricingDifferentiationFeaturePills?: boolean;
37
+ isExperimentVariant?: boolean;
32
38
  } ) => { [ planSlug: string ]: PlanFeaturesForGridPlan };
33
39
 
34
40
  const useRestructuredPlanFeaturesForComparisonGrid: UseRestructuredPlanFeaturesForComparisonGrid =
@@ -39,6 +45,9 @@ const useRestructuredPlanFeaturesForComparisonGrid: UseRestructuredPlanFeaturesF
39
45
  intent,
40
46
  selectedFeature,
41
47
  showLegacyStorageFeature,
48
+ useVar42NoAiFeatures,
49
+ showPricingDifferentiationFeaturePills,
50
+ isExperimentVariant,
42
51
  } ) => {
43
52
  const planFeaturesForGridPlans = usePlanFeaturesForGridPlans( {
44
53
  gridPlans,
@@ -46,6 +55,9 @@ const useRestructuredPlanFeaturesForComparisonGrid: UseRestructuredPlanFeaturesF
46
55
  intent,
47
56
  selectedFeature,
48
57
  showLegacyStorageFeature,
58
+ useVar42NoAiFeatures,
59
+ showPricingDifferentiationFeaturePills,
60
+ isExperimentVariant,
49
61
  } );
50
62
 
51
63
  return useMemo( () => {
@@ -58,26 +70,78 @@ const useRestructuredPlanFeaturesForComparisonGrid: UseRestructuredPlanFeaturesF
58
70
  const annualPlansOnlyFeatures = planConstantObj.getAnnualPlansOnlyFeatures?.();
59
71
  const isMonthlyPlan = isMonthly( planSlug );
60
72
 
61
- const wpcomFeatures = planConstantObj.get2023PlanComparisonFeatureOverride?.().length
62
- ? getPlanFeaturesObject(
73
+ let wpcomFeatures;
74
+
75
+ // Plans differentiators (non-control): use experiment comparison override when present.
76
+ if (
77
+ isExperimentVariant &&
78
+ planConstantObj.get2023PlanComparisonFeatureOverrideForExperiment?.()?.length
79
+ ) {
80
+ wpcomFeatures = getPlanFeaturesObject(
81
+ allFeaturesList,
82
+ planConstantObj.get2023PlanComparisonFeatureOverrideForExperiment().slice(),
83
+ isExperimentVariant
84
+ );
85
+ } else if (
86
+ // Check if there's a specific override for comparison
87
+ planConstantObj.get2023PlanComparisonFeatureOverride?.().length
88
+ ) {
89
+ wpcomFeatures = getPlanFeaturesObject(
90
+ allFeaturesList,
91
+ planConstantObj.get2023PlanComparisonFeatureOverride().slice()
92
+ );
93
+ } else if ( 'plans-wordpress-hosting' === intent ) {
94
+ // Use visual split features for WordPress hosting intent
95
+ if ( planConstantObj?.getVisualSplitBusinessFeatures ) {
96
+ wpcomFeatures = getPlanFeaturesObject(
63
97
  allFeaturesList,
64
- planConstantObj.get2023PlanComparisonFeatureOverride().slice()
65
- )
66
- : getPlanFeaturesObject(
98
+ planConstantObj.getVisualSplitBusinessFeatures().slice()
99
+ );
100
+ } else if ( planConstantObj?.getVisualSplitCommerceFeatures ) {
101
+ wpcomFeatures = getPlanFeaturesObject(
67
102
  allFeaturesList,
68
- planConstantObj.get2023PricingGridSignupWpcomFeatures?.().slice()
69
- );
70
-
71
- const jetpackFeatures = planConstantObj.get2023PlanComparisonJetpackFeatureOverride?.()
72
- .length
73
- ? getPlanFeaturesObject(
103
+ planConstantObj.getVisualSplitCommerceFeatures().slice()
104
+ );
105
+ } else {
106
+ // Fallback to default features
107
+ wpcomFeatures = getPlanFeaturesObject(
74
108
  allFeaturesList,
75
- planConstantObj.get2023PlanComparisonJetpackFeatureOverride().slice()
76
- )
77
- : getPlanFeaturesObject(
78
- allFeaturesList,
79
- planConstantObj.get2023PricingGridSignupJetpackFeatures?.().slice()
80
- );
109
+ planConstantObj.get2023PricingGridSignupWpcomFeatures?.().slice()
110
+ );
111
+ }
112
+ } else {
113
+ // Default case
114
+ wpcomFeatures = getPlanFeaturesObject(
115
+ allFeaturesList,
116
+ planConstantObj.get2023PricingGridSignupWpcomFeatures?.().slice()
117
+ );
118
+ }
119
+
120
+ // Plans Differentiators Experiment: For comparison grid, use dedicated experiment override function
121
+ // when in an experiment variant for Jetpack features.
122
+ // Note: We check if the function exists, not if it returns a non-empty array, because an empty array
123
+ // is a valid return value (meaning "show no Jetpack features").
124
+ let jetpackFeatures;
125
+ if (
126
+ isExperimentVariant &&
127
+ planConstantObj.get2023PlanComparisonJetpackFeatureOverrideForExperiment
128
+ ) {
129
+ jetpackFeatures = getPlanFeaturesObject(
130
+ allFeaturesList,
131
+ planConstantObj.get2023PlanComparisonJetpackFeatureOverrideForExperiment().slice(),
132
+ isExperimentVariant
133
+ );
134
+ } else if ( planConstantObj.get2023PlanComparisonJetpackFeatureOverride?.().length ) {
135
+ jetpackFeatures = getPlanFeaturesObject(
136
+ allFeaturesList,
137
+ planConstantObj.get2023PlanComparisonJetpackFeatureOverride().slice()
138
+ );
139
+ } else {
140
+ jetpackFeatures = getPlanFeaturesObject(
141
+ allFeaturesList,
142
+ planConstantObj.get2023PricingGridSignupJetpackFeatures?.().slice()
143
+ );
144
+ }
81
145
 
82
146
  const wpcomFeaturesTransformed: TransformedFeatureObject[] | null | undefined =
83
147
  annualPlansOnlyFeatures
@@ -145,14 +209,23 @@ const useRestructuredPlanFeaturesForComparisonGrid: UseRestructuredPlanFeaturesF
145
209
  ...previousPlanFeatures.jetpackFeatures,
146
210
  ],
147
211
  storageFeature: planFeaturesForGridPlans[ planSlug ].storageFeature,
148
- comparisonGridFeatureLabels: planConstantObj.getPlanComparisonFeatureLabels?.(),
212
+ comparisonGridFeatureLabels: planConstantObj.getPlanComparisonFeatureLabels?.( {
213
+ isExperimentVariant,
214
+ } ),
149
215
  };
150
216
 
151
217
  previousPlan = planSlug;
152
218
  }
153
219
 
154
220
  return planFeatureMap;
155
- }, [ gridPlans, allFeaturesList, planFeaturesForGridPlans, intent, hasRedeemedDomainCredit ] );
221
+ }, [
222
+ gridPlans,
223
+ allFeaturesList,
224
+ planFeaturesForGridPlans,
225
+ intent,
226
+ hasRedeemedDomainCredit,
227
+ isExperimentVariant,
228
+ ] );
156
229
  };
157
230
 
158
231
  export default useRestructuredPlanFeaturesForComparisonGrid;
@@ -0,0 +1,31 @@
1
+ import { isWooHostedBasicPlan, type PlanSlug } from '@automattic/calypso-products';
2
+ import { useTranslate } from 'i18n-calypso';
3
+ import type { PlansIntent } from '../../types';
4
+ import type { TranslateResult } from 'i18n-calypso';
5
+
6
+ interface Props {
7
+ intent?: PlansIntent;
8
+ planSlugs: PlanSlug[];
9
+ }
10
+
11
+ const useTitleBadges = ( { intent, planSlugs }: Props ) => {
12
+ const translate = useTranslate();
13
+
14
+ return planSlugs.reduce(
15
+ ( acc, planSlug ) => {
16
+ let label;
17
+
18
+ if ( 'plans-woo-hosted' === intent && isWooHostedBasicPlan( planSlug ) ) {
19
+ label = translate( 'Recommended' );
20
+ }
21
+
22
+ return {
23
+ ...acc,
24
+ [ planSlug ]: label ?? null,
25
+ };
26
+ },
27
+ {} as Record< PlanSlug, TranslateResult | null >
28
+ );
29
+ };
30
+
31
+ export default useTitleBadges;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import { PlanSlug } from '@automattic/calypso-products';
6
+ import { renderHook } from '@testing-library/react';
7
+ import React from 'react';
8
+ import PlansGridContextProvider from '../../grid-context';
9
+ import { GridPlan } from '../../types';
10
+ import { useVisibleGridPlans } from '../use-visible-grid-plans';
11
+ import type { TranslateResult } from 'i18n-calypso';
12
+
13
+ // Mock the hooks we're using
14
+ jest.mock( '@automattic/data-stores', () => ( {
15
+ Plans: {
16
+ useCurrentPlanTerm: jest.fn(),
17
+ },
18
+ } ) );
19
+
20
+ jest.mock( '../../hooks/data-store/use-plan-billing-period', () => ( {
21
+ __esModule: true,
22
+ default: jest.fn(),
23
+ } ) );
24
+
25
+ // Helper to create a mock GridPlan
26
+ const createMockGridPlan = ( slug: PlanSlug ): GridPlan => ( {
27
+ planSlug: slug,
28
+ pricing: {
29
+ originalPrice: { monthly: 10, full: 20 },
30
+ discountedPrice: { monthly: 5, full: 10 },
31
+ },
32
+ isVisible: true,
33
+ features: {
34
+ wpcomFeatures: [],
35
+ jetpackFeatures: [],
36
+ },
37
+ tagline: '',
38
+ planTitle: '',
39
+ availableForPurchase: true,
40
+ } );
41
+
42
+ describe( 'useVisibleGridPlans', () => {
43
+ const mockGridPlans = [
44
+ createMockGridPlan( 'free_plan' ),
45
+ createMockGridPlan( 'personal-bundle' ),
46
+ createMockGridPlan( 'value_bundle' ),
47
+ createMockGridPlan( 'business-bundle' ),
48
+ createMockGridPlan( 'ecommerce-bundle' ),
49
+ ];
50
+
51
+ const wrapper = ( { children }: { children: React.ReactNode } ) => (
52
+ <PlansGridContextProvider
53
+ gridPlans={ mockGridPlans }
54
+ intent={ undefined }
55
+ siteId={ 2345 }
56
+ allFeaturesList={ {} }
57
+ useCheckPlanAvailabilityForPurchase={ () => ( {} ) }
58
+ useAction={ () => ( {
59
+ primary: {
60
+ text: '' as TranslateResult,
61
+ callback: () => {},
62
+ },
63
+ } ) }
64
+ featureGroupMap={ {} }
65
+ >
66
+ { children }
67
+ </PlansGridContextProvider>
68
+ );
69
+
70
+ test( 'should show correct number of plans based on grid size', () => {
71
+ const { result } = renderHook(
72
+ () => useVisibleGridPlans( { gridSize: 'small', intervalType: 'yearly', siteId: 2345 } ),
73
+ { wrapper }
74
+ );
75
+
76
+ expect( result.current.visibleGridPlans ).toHaveLength( 2 );
77
+ } );
78
+
79
+ test( 'should always include current plan if it exists', () => {
80
+ const { result } = renderHook(
81
+ () =>
82
+ useVisibleGridPlans( {
83
+ gridSize: 'small',
84
+ currentSitePlanSlug: 'value_bundle',
85
+ intervalType: 'yearly',
86
+ siteId: 2345,
87
+ } ),
88
+ { wrapper }
89
+ );
90
+
91
+ expect( result.current.visibleGridPlans.map( ( plan ) => plan.planSlug ) ).toContain(
92
+ 'value_bundle'
93
+ );
94
+ expect( result.current.visibleGridPlans ).toHaveLength( 2 );
95
+ } );
96
+
97
+ test( 'should handle grid size changes correctly', () => {
98
+ const { result, rerender } = renderHook(
99
+ ( { size }: { size: 'small' | 'large' } ) =>
100
+ useVisibleGridPlans( {
101
+ gridSize: size,
102
+ currentSitePlanSlug: 'value_bundle',
103
+ intervalType: 'yearly',
104
+ siteId: 2345,
105
+ } ),
106
+ {
107
+ wrapper,
108
+ initialProps: { size: 'small' },
109
+ }
110
+ );
111
+
112
+ expect( result.current.visibleGridPlans ).toHaveLength( 2 );
113
+ rerender( { size: 'large' } );
114
+ expect( result.current.visibleGridPlans ).toHaveLength( 4 );
115
+ } );
116
+ } );
@@ -1,4 +1,5 @@
1
1
  import { useLayoutEffect, useState } from '@wordpress/element';
2
+ import { GridSize } from '../types';
2
3
 
3
4
  interface Props {
4
5
  containerRef: React.MutableRefObject< HTMLDivElement | null >;
@@ -8,7 +9,7 @@ interface Props {
8
9
  * but they could be used in the future in a containment context.
9
10
  * The keys are the labels, the values are the minimum widths.
10
11
  */
11
- containerBreakpoints: Map< string, number >;
12
+ containerBreakpoints: Map< GridSize, number >;
12
13
  }
13
14
 
14
15
  /**
@@ -16,7 +17,7 @@ interface Props {
16
17
  * and the breakpoints passed through as props.
17
18
  */
18
19
  export default function useGridSize( { containerRef, containerBreakpoints }: Props ) {
19
- const [ gridSize, setGridSize ] = useState< string | null >( null );
20
+ const [ gridSize, setGridSize ] = useState< GridSize | null >( null );
20
21
 
21
22
  useLayoutEffect( () => {
22
23
  if ( ! containerRef.current ) {
@@ -1,5 +1,5 @@
1
+ import { formatCurrency } from '@automattic/number-formatters';
1
2
  import { useMemo } from '@wordpress/element';
2
- import { formatCurrency } from 'i18n-calypso';
3
3
  const LARGE_ADD_ON_CURRENCY_CHAR_THRESHOLD = 7;
4
4
  const LARGE_CURRENCY_CHAR_THRESHOLD = 6;
5
5
  const LARGE_CURRENCY_COMBINED_CHAR_THRESHOLD = 9;
@@ -0,0 +1,102 @@
1
+ // hooks/use-visible-grid-plans.ts
2
+ import { getPlanClass } from '@automattic/calypso-products';
3
+ import { Plans } from '@automattic/data-stores';
4
+ import { useState, useEffect } from 'react';
5
+ import { usePlansGridContext } from '../grid-context';
6
+ import { GridPlan, GridSize, SupportedUrlFriendlyTermType } from '../types';
7
+ import usePlanBillingPeriod from './data-store/use-plan-billing-period';
8
+ import type { PlanSlug } from '@automattic/calypso-products';
9
+
10
+ interface UseVisibleGridPlansProps {
11
+ gridSize: GridSize | undefined;
12
+ currentSitePlanSlug?: PlanSlug | null;
13
+ siteId: number | null | undefined;
14
+ intervalType: SupportedUrlFriendlyTermType;
15
+ }
16
+
17
+ export function useVisibleGridPlans( {
18
+ gridSize,
19
+ currentSitePlanSlug,
20
+ siteId,
21
+ intervalType,
22
+ }: UseVisibleGridPlansProps ) {
23
+ const [ visibleGridPlans, setVisibleGridPlans ] = useState< GridPlan[] >( [] );
24
+ const { gridPlans, gridPlansIndex } = usePlansGridContext();
25
+ const currentPlanTerm = Plans.useCurrentPlanTerm( { siteId } );
26
+ const selectedPlanTerm = usePlanBillingPeriod( { intervalType } );
27
+
28
+ useEffect( () => {
29
+ setVisibleGridPlans( ( previousGridPlans ) => {
30
+ let visibleLength = gridPlans.length;
31
+
32
+ switch ( gridSize ) {
33
+ case 'large':
34
+ visibleLength = 4;
35
+ break;
36
+ case 'medium':
37
+ visibleLength = 3;
38
+ break;
39
+ case 'smedium':
40
+ case 'small':
41
+ visibleLength = 2;
42
+ break;
43
+ }
44
+
45
+ // Find the user's current plan in the current term
46
+ const usersGridPlanFromSelectedTerm = currentSitePlanSlug
47
+ ? gridPlans.find(
48
+ ( gridPlan ) =>
49
+ getPlanClass( gridPlan.planSlug ) === getPlanClass( currentSitePlanSlug )
50
+ )
51
+ : null;
52
+
53
+ // Check if previous state is stale
54
+ const isPreviousGridPlansStale = previousGridPlans.some(
55
+ ( plan ) => ! gridPlansIndex[ plan.planSlug ]
56
+ );
57
+
58
+ let nextGridPlans: GridPlan[] = previousGridPlans;
59
+
60
+ if ( previousGridPlans.length !== visibleLength ) {
61
+ nextGridPlans = gridPlans.slice( 0, visibleLength );
62
+ } else if ( isPreviousGridPlansStale ) {
63
+ // Map existing plans to their new term equivalents, preserving order
64
+ nextGridPlans = previousGridPlans.map( ( plan ) => {
65
+ const gridPlan = gridPlans.find(
66
+ ( gridPlan ) => getPlanClass( gridPlan.planSlug ) === getPlanClass( plan.planSlug )
67
+ );
68
+ return gridPlan ?? plan;
69
+ } );
70
+ }
71
+
72
+ // Ensure current plan is visible
73
+ if ( usersGridPlanFromSelectedTerm ) {
74
+ const isCurrentPlanVisible = nextGridPlans.some(
75
+ ( plan ) =>
76
+ getPlanClass( plan.planSlug ) === getPlanClass( usersGridPlanFromSelectedTerm.planSlug )
77
+ );
78
+
79
+ if ( ! isCurrentPlanVisible ) {
80
+ nextGridPlans = [ usersGridPlanFromSelectedTerm, ...nextGridPlans ].slice(
81
+ 0,
82
+ visibleLength
83
+ );
84
+ }
85
+ }
86
+
87
+ return nextGridPlans;
88
+ } );
89
+ }, [
90
+ gridSize,
91
+ gridPlans,
92
+ currentSitePlanSlug,
93
+ currentPlanTerm,
94
+ selectedPlanTerm,
95
+ gridPlansIndex,
96
+ ] );
97
+
98
+ return {
99
+ visibleGridPlans,
100
+ setVisibleGridPlans,
101
+ };
102
+ }
package/src/index.tsx CHANGED
@@ -9,10 +9,18 @@ import useGridPlans, { usePlanTypesWithIntent } from './hooks/data-store/use-gri
9
9
  import useGridPlansForComparisonGrid from './hooks/data-store/use-grid-plans-for-comparison-grid';
10
10
  import useGridPlansForFeaturesGrid from './hooks/data-store/use-grid-plans-for-features-grid';
11
11
  import usePlanBillingDescription from './hooks/data-store/use-plan-billing-description';
12
+ import usePlanBillingPeriod from './hooks/data-store/use-plan-billing-period';
12
13
  import usePlanFeaturesForGridPlans from './hooks/data-store/use-plan-features-for-grid-plans';
13
14
  import usePlansFromTypes from './hooks/data-store/use-plans-from-types';
14
15
  import useRestructuredPlanFeaturesForComparisonGrid from './hooks/data-store/use-restructured-plan-features-for-comparison-grid';
15
16
  import { useManageTooltipToggle } from './hooks/use-manage-tooltip-toggle';
17
+ import {
18
+ getPlanPriceForDuration,
19
+ calculateDiscountPercentage,
20
+ fromPricingMetaForGridPlan,
21
+ fromVariantPriceData,
22
+ } from './lib/plan-pricing-utils';
23
+ import type { PlanPriceInfo, VariantPriceData } from './lib/plan-pricing-utils';
16
24
 
17
25
  /**
18
26
  * Types
@@ -33,6 +41,7 @@ export {
33
41
  useGridPlansForFeaturesGrid,
34
42
  useGridPlansForComparisonGrid,
35
43
  useGridPlanForSpotlight,
44
+ usePlanBillingPeriod,
36
45
  usePlanBillingDescription,
37
46
  usePlanFeaturesForGridPlans,
38
47
  usePlansFromTypes,
@@ -44,3 +53,14 @@ export {
44
53
  * Constants
45
54
  */
46
55
  export { EFFECTIVE_TERMS_LIST };
56
+
57
+ /**
58
+ * Plan pricing utilities
59
+ */
60
+ export type { PlanPriceInfo, VariantPriceData };
61
+ export {
62
+ getPlanPriceForDuration,
63
+ calculateDiscountPercentage,
64
+ fromPricingMetaForGridPlan,
65
+ fromVariantPriceData,
66
+ };