@automattic/plans-grid-next 1.0.2 → 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 (298) 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 +99 -92
  4. package/dist/cjs/components/comparison-grid/index.js.map +1 -1
  5. package/dist/cjs/components/comparison-grid/style.scss +10 -2
  6. package/dist/cjs/components/features-grid/client-logo-list/client-list.js +0 -12
  7. package/dist/cjs/components/features-grid/client-logo-list/client-list.js.map +1 -1
  8. package/dist/cjs/components/features-grid/index.js +9 -6
  9. package/dist/cjs/components/features-grid/index.js.map +1 -1
  10. package/dist/cjs/components/features-grid/plan-features-list.js +10 -3
  11. package/dist/cjs/components/features-grid/plan-features-list.js.map +1 -1
  12. package/dist/cjs/components/features-grid/plan-headers.js +2 -2
  13. package/dist/cjs/components/features-grid/plan-headers.js.map +1 -1
  14. package/dist/cjs/components/features-grid/plan-tagline.js +1 -1
  15. package/dist/cjs/components/features-grid/plan-tagline.js.map +1 -1
  16. package/dist/cjs/components/features-grid/style.scss +107 -19
  17. package/dist/cjs/components/features-grid/table.js +1 -1
  18. package/dist/cjs/components/features-grid/table.js.map +1 -1
  19. package/dist/cjs/components/features.js +43 -4
  20. package/dist/cjs/components/features.js.map +1 -1
  21. package/dist/cjs/components/item.js +1 -1
  22. package/dist/cjs/components/item.js.map +1 -1
  23. package/dist/cjs/components/plan-button/index.js +5 -3
  24. package/dist/cjs/components/plan-button/index.js.map +1 -1
  25. package/dist/cjs/components/plan-button/style.scss +75 -51
  26. package/dist/cjs/components/plan-div-td-container.js +4 -1
  27. package/dist/cjs/components/plan-div-td-container.js.map +1 -1
  28. package/dist/cjs/components/plan-logo.js +6 -3
  29. package/dist/cjs/components/plan-logo.js.map +1 -1
  30. package/dist/cjs/components/plan-type-selector/components/interval-type-dropdown.js +12 -1
  31. package/dist/cjs/components/plan-type-selector/components/interval-type-dropdown.js.map +1 -1
  32. package/dist/cjs/components/plan-type-selector/hooks/use-max-discount.js +4 -33
  33. package/dist/cjs/components/plan-type-selector/hooks/use-max-discount.js.map +1 -1
  34. package/dist/cjs/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.js +11 -13
  35. package/dist/cjs/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.js.map +1 -1
  36. package/dist/cjs/components/plans-2023-tooltip.js +16 -5
  37. package/dist/cjs/components/plans-2023-tooltip.js.map +1 -1
  38. package/dist/cjs/components/shared/action-button/index.js +22 -7
  39. package/dist/cjs/components/shared/action-button/index.js.map +1 -1
  40. package/dist/cjs/components/shared/action-button/style.scss +4 -0
  41. package/dist/cjs/components/shared/billing-timeframe/index.js +8 -4
  42. package/dist/cjs/components/shared/billing-timeframe/index.js.map +1 -1
  43. package/dist/cjs/components/shared/header-price/index.js +60 -15
  44. package/dist/cjs/components/shared/header-price/index.js.map +1 -1
  45. package/dist/cjs/components/shared/header-price/style.scss +9 -3
  46. package/dist/cjs/components/shared/storage/components/plan-storage.js +2 -2
  47. package/dist/cjs/components/shared/storage/components/plan-storage.js.map +1 -1
  48. package/dist/cjs/components/shared/storage/components/storage-dropdown.js +29 -6
  49. package/dist/cjs/components/shared/storage/components/storage-dropdown.js.map +1 -1
  50. package/dist/cjs/components/shared/storage/components/storage-feature-label.js +2 -1
  51. package/dist/cjs/components/shared/storage/components/storage-feature-label.js.map +1 -1
  52. package/dist/cjs/components/shared/storage/hooks/use-plan-storage.js +2 -0
  53. package/dist/cjs/components/shared/storage/hooks/use-plan-storage.js.map +1 -1
  54. package/dist/cjs/fixtures/sites-purchases.js +2 -4
  55. package/dist/cjs/fixtures/sites-purchases.js.map +1 -1
  56. package/dist/cjs/grid-context.js +4 -1
  57. package/dist/cjs/grid-context.js.map +1 -1
  58. package/dist/cjs/hooks/data-store/get-renewal-pricing-text.js +50 -0
  59. package/dist/cjs/hooks/data-store/get-renewal-pricing-text.js.map +1 -0
  60. package/dist/cjs/hooks/data-store/use-grid-plans-for-comparison-grid.js +6 -1
  61. package/dist/cjs/hooks/data-store/use-grid-plans-for-comparison-grid.js.map +1 -1
  62. package/dist/cjs/hooks/data-store/use-grid-plans-for-features-grid.js +6 -1
  63. package/dist/cjs/hooks/data-store/use-grid-plans-for-features-grid.js.map +1 -1
  64. package/dist/cjs/hooks/data-store/use-grid-plans.js +175 -21
  65. package/dist/cjs/hooks/data-store/use-grid-plans.js.map +1 -1
  66. package/dist/cjs/hooks/data-store/use-highlight-labels.js +13 -4
  67. package/dist/cjs/hooks/data-store/use-highlight-labels.js.map +1 -1
  68. package/dist/cjs/hooks/data-store/use-plan-billing-description.js +68 -13
  69. package/dist/cjs/hooks/data-store/use-plan-billing-description.js.map +1 -1
  70. package/dist/cjs/hooks/data-store/use-plan-features-for-grid-plans.js +76 -2
  71. package/dist/cjs/hooks/data-store/use-plan-features-for-grid-plans.js.map +1 -1
  72. package/dist/cjs/hooks/data-store/use-restructured-plan-features-for-comparison-grid.js +60 -12
  73. package/dist/cjs/hooks/data-store/use-restructured-plan-features-for-comparison-grid.js.map +1 -1
  74. package/dist/cjs/hooks/data-store/use-title-badges.js +19 -0
  75. package/dist/cjs/hooks/data-store/use-title-badges.js.map +1 -0
  76. package/dist/cjs/hooks/use-is-large-currency.js +2 -2
  77. package/dist/cjs/hooks/use-is-large-currency.js.map +1 -1
  78. package/dist/cjs/hooks/use-visible-grid-plans.js +70 -0
  79. package/dist/cjs/hooks/use-visible-grid-plans.js.map +1 -0
  80. package/dist/cjs/index.js +6 -1
  81. package/dist/cjs/index.js.map +1 -1
  82. package/dist/cjs/lib/get-plan-features-object.js +15 -2
  83. package/dist/cjs/lib/get-plan-features-object.js.map +1 -1
  84. package/dist/cjs/lib/plan-pricing-utils.js +135 -0
  85. package/dist/cjs/lib/plan-pricing-utils.js.map +1 -0
  86. package/dist/esm/_shared.scss +4 -3
  87. package/dist/esm/components/comparison-grid/index.js +100 -93
  88. package/dist/esm/components/comparison-grid/index.js.map +1 -1
  89. package/dist/esm/components/comparison-grid/style.scss +10 -2
  90. package/dist/esm/components/features-grid/client-logo-list/client-list.js +0 -12
  91. package/dist/esm/components/features-grid/client-logo-list/client-list.js.map +1 -1
  92. package/dist/esm/components/features-grid/index.js +9 -6
  93. package/dist/esm/components/features-grid/index.js.map +1 -1
  94. package/dist/esm/components/features-grid/plan-features-list.js +10 -3
  95. package/dist/esm/components/features-grid/plan-features-list.js.map +1 -1
  96. package/dist/esm/components/features-grid/plan-headers.js +3 -3
  97. package/dist/esm/components/features-grid/plan-headers.js.map +1 -1
  98. package/dist/esm/components/features-grid/plan-tagline.js +1 -1
  99. package/dist/esm/components/features-grid/plan-tagline.js.map +1 -1
  100. package/dist/esm/components/features-grid/style.scss +107 -19
  101. package/dist/esm/components/features-grid/table.js +1 -1
  102. package/dist/esm/components/features-grid/table.js.map +1 -1
  103. package/dist/esm/components/features.js +44 -5
  104. package/dist/esm/components/features.js.map +1 -1
  105. package/dist/esm/components/item.js +1 -1
  106. package/dist/esm/components/item.js.map +1 -1
  107. package/dist/esm/components/plan-button/index.js +5 -3
  108. package/dist/esm/components/plan-button/index.js.map +1 -1
  109. package/dist/esm/components/plan-button/style.scss +75 -51
  110. package/dist/esm/components/plan-div-td-container.js +4 -1
  111. package/dist/esm/components/plan-div-td-container.js.map +1 -1
  112. package/dist/esm/components/plan-logo.js +7 -4
  113. package/dist/esm/components/plan-logo.js.map +1 -1
  114. package/dist/esm/components/plan-type-selector/components/interval-type-dropdown.js +12 -1
  115. package/dist/esm/components/plan-type-selector/components/interval-type-dropdown.js.map +1 -1
  116. package/dist/esm/components/plan-type-selector/hooks/use-max-discount.js +3 -33
  117. package/dist/esm/components/plan-type-selector/hooks/use-max-discount.js.map +1 -1
  118. package/dist/esm/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.js +11 -13
  119. package/dist/esm/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.js.map +1 -1
  120. package/dist/esm/components/plans-2023-tooltip.js +16 -5
  121. package/dist/esm/components/plans-2023-tooltip.js.map +1 -1
  122. package/dist/esm/components/shared/action-button/index.js +22 -7
  123. package/dist/esm/components/shared/action-button/index.js.map +1 -1
  124. package/dist/esm/components/shared/action-button/style.scss +4 -0
  125. package/dist/esm/components/shared/billing-timeframe/index.js +8 -4
  126. package/dist/esm/components/shared/billing-timeframe/index.js.map +1 -1
  127. package/dist/esm/components/shared/header-price/index.js +60 -15
  128. package/dist/esm/components/shared/header-price/index.js.map +1 -1
  129. package/dist/esm/components/shared/header-price/style.scss +9 -3
  130. package/dist/esm/components/shared/storage/components/plan-storage.js +2 -2
  131. package/dist/esm/components/shared/storage/components/plan-storage.js.map +1 -1
  132. package/dist/esm/components/shared/storage/components/storage-dropdown.js +30 -7
  133. package/dist/esm/components/shared/storage/components/storage-dropdown.js.map +1 -1
  134. package/dist/esm/components/shared/storage/components/storage-feature-label.js +2 -1
  135. package/dist/esm/components/shared/storage/components/storage-feature-label.js.map +1 -1
  136. package/dist/esm/components/shared/storage/hooks/use-plan-storage.js +3 -1
  137. package/dist/esm/components/shared/storage/hooks/use-plan-storage.js.map +1 -1
  138. package/dist/esm/fixtures/sites-purchases.js +2 -4
  139. package/dist/esm/fixtures/sites-purchases.js.map +1 -1
  140. package/dist/esm/grid-context.js +4 -1
  141. package/dist/esm/grid-context.js.map +1 -1
  142. package/dist/esm/hooks/data-store/get-renewal-pricing-text.js +47 -0
  143. package/dist/esm/hooks/data-store/get-renewal-pricing-text.js.map +1 -0
  144. package/dist/esm/hooks/data-store/use-grid-plans-for-comparison-grid.js +6 -1
  145. package/dist/esm/hooks/data-store/use-grid-plans-for-comparison-grid.js.map +1 -1
  146. package/dist/esm/hooks/data-store/use-grid-plans-for-features-grid.js +6 -1
  147. package/dist/esm/hooks/data-store/use-grid-plans-for-features-grid.js.map +1 -1
  148. package/dist/esm/hooks/data-store/use-grid-plans.js +176 -22
  149. package/dist/esm/hooks/data-store/use-grid-plans.js.map +1 -1
  150. package/dist/esm/hooks/data-store/use-highlight-labels.js +14 -5
  151. package/dist/esm/hooks/data-store/use-highlight-labels.js.map +1 -1
  152. package/dist/esm/hooks/data-store/use-plan-billing-description.js +66 -11
  153. package/dist/esm/hooks/data-store/use-plan-billing-description.js.map +1 -1
  154. package/dist/esm/hooks/data-store/use-plan-features-for-grid-plans.js +77 -3
  155. package/dist/esm/hooks/data-store/use-plan-features-for-grid-plans.js.map +1 -1
  156. package/dist/esm/hooks/data-store/use-restructured-plan-features-for-comparison-grid.js +59 -11
  157. package/dist/esm/hooks/data-store/use-restructured-plan-features-for-comparison-grid.js.map +1 -1
  158. package/dist/esm/hooks/data-store/use-title-badges.js +17 -0
  159. package/dist/esm/hooks/data-store/use-title-badges.js.map +1 -0
  160. package/dist/esm/hooks/use-is-large-currency.js +1 -1
  161. package/dist/esm/hooks/use-is-large-currency.js.map +1 -1
  162. package/dist/esm/hooks/use-visible-grid-plans.js +66 -0
  163. package/dist/esm/hooks/use-visible-grid-plans.js.map +1 -0
  164. package/dist/esm/index.js +2 -0
  165. package/dist/esm/index.js.map +1 -1
  166. package/dist/esm/lib/get-plan-features-object.js +15 -2
  167. package/dist/esm/lib/get-plan-features-object.js.map +1 -1
  168. package/dist/esm/lib/plan-pricing-utils.js +129 -0
  169. package/dist/esm/lib/plan-pricing-utils.js.map +1 -0
  170. package/dist/tsconfig-cjs.tsbuildinfo +1 -1
  171. package/dist/tsconfig.tsbuildinfo +1 -1
  172. package/dist/types/components/comparison-grid/index.d.ts +1 -1
  173. package/dist/types/components/comparison-grid/index.d.ts.map +1 -1
  174. package/dist/types/components/features-grid/client-logo-list/client-list.d.ts.map +1 -1
  175. package/dist/types/components/features-grid/index.d.ts.map +1 -1
  176. package/dist/types/components/features-grid/plan-features-list.d.ts.map +1 -1
  177. package/dist/types/components/features-grid/plan-headers.d.ts +2 -0
  178. package/dist/types/components/features-grid/plan-headers.d.ts.map +1 -1
  179. package/dist/types/components/features-grid/table.d.ts.map +1 -1
  180. package/dist/types/components/features.d.ts.map +1 -1
  181. package/dist/types/components/item.d.ts +2 -1
  182. package/dist/types/components/item.d.ts.map +1 -1
  183. package/dist/types/components/plan-button/index.d.ts +2 -1
  184. package/dist/types/components/plan-button/index.d.ts.map +1 -1
  185. package/dist/types/components/plan-div-td-container.d.ts +2 -0
  186. package/dist/types/components/plan-div-td-container.d.ts.map +1 -1
  187. package/dist/types/components/plan-logo.d.ts.map +1 -1
  188. package/dist/types/components/plan-type-selector/components/interval-type-dropdown.d.ts.map +1 -1
  189. package/dist/types/components/plan-type-selector/hooks/use-max-discount.d.ts.map +1 -1
  190. package/dist/types/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.d.ts.map +1 -1
  191. package/dist/types/components/plans-2023-tooltip.d.ts.map +1 -1
  192. package/dist/types/components/shared/action-button/index.d.ts +2 -1
  193. package/dist/types/components/shared/action-button/index.d.ts.map +1 -1
  194. package/dist/types/components/shared/billing-timeframe/index.d.ts.map +1 -1
  195. package/dist/types/components/shared/header-price/index.d.ts.map +1 -1
  196. package/dist/types/components/shared/storage/components/storage-dropdown.d.ts.map +1 -1
  197. package/dist/types/components/shared/storage/components/storage-feature-label.d.ts.map +1 -1
  198. package/dist/types/components/shared/storage/hooks/use-plan-storage.d.ts +1 -1
  199. package/dist/types/components/shared/storage/hooks/use-plan-storage.d.ts.map +1 -1
  200. package/dist/types/fixtures/sites-purchases.d.ts +2 -4
  201. package/dist/types/fixtures/sites-purchases.d.ts.map +1 -1
  202. package/dist/types/grid-context.d.ts +4 -1
  203. package/dist/types/grid-context.d.ts.map +1 -1
  204. package/dist/types/hooks/data-store/get-renewal-pricing-text.d.ts +14 -0
  205. package/dist/types/hooks/data-store/get-renewal-pricing-text.d.ts.map +1 -0
  206. package/dist/types/hooks/data-store/types.d.ts +21 -0
  207. package/dist/types/hooks/data-store/types.d.ts.map +1 -1
  208. package/dist/types/hooks/data-store/use-grid-plans-for-comparison-grid.d.ts +1 -1
  209. package/dist/types/hooks/data-store/use-grid-plans-for-comparison-grid.d.ts.map +1 -1
  210. package/dist/types/hooks/data-store/use-grid-plans-for-features-grid.d.ts +1 -1
  211. package/dist/types/hooks/data-store/use-grid-plans-for-features-grid.d.ts.map +1 -1
  212. package/dist/types/hooks/data-store/use-grid-plans.d.ts.map +1 -1
  213. package/dist/types/hooks/data-store/use-highlight-labels.d.ts.map +1 -1
  214. package/dist/types/hooks/data-store/use-plan-billing-description.d.ts.map +1 -1
  215. package/dist/types/hooks/data-store/use-plan-features-for-grid-plans.d.ts +4 -1
  216. package/dist/types/hooks/data-store/use-plan-features-for-grid-plans.d.ts.map +1 -1
  217. package/dist/types/hooks/data-store/use-restructured-plan-features-for-comparison-grid.d.ts +4 -1
  218. package/dist/types/hooks/data-store/use-restructured-plan-features-for-comparison-grid.d.ts.map +1 -1
  219. package/dist/types/hooks/data-store/use-title-badges.d.ts +9 -0
  220. package/dist/types/hooks/data-store/use-title-badges.d.ts.map +1 -0
  221. package/dist/types/hooks/use-visible-grid-plans.d.ts +14 -0
  222. package/dist/types/hooks/use-visible-grid-plans.d.ts.map +1 -0
  223. package/dist/types/index.d.ts +7 -0
  224. package/dist/types/index.d.ts.map +1 -1
  225. package/dist/types/lib/get-plan-features-object.d.ts +1 -1
  226. package/dist/types/lib/get-plan-features-object.d.ts.map +1 -1
  227. package/dist/types/lib/plan-pricing-utils.d.ts +105 -0
  228. package/dist/types/lib/plan-pricing-utils.d.ts.map +1 -0
  229. package/dist/types/types.d.ts +29 -2
  230. package/dist/types/types.d.ts.map +1 -1
  231. package/package.json +38 -28
  232. package/src/_shared.scss +4 -3
  233. package/src/components/comparison-grid/index.tsx +258 -181
  234. package/src/components/comparison-grid/style.scss +10 -2
  235. package/src/components/features-grid/client-logo-list/client-list.tsx +0 -25
  236. package/src/components/features-grid/index.tsx +35 -18
  237. package/src/components/features-grid/plan-features-list.tsx +15 -4
  238. package/src/components/features-grid/plan-headers.tsx +10 -3
  239. package/src/components/features-grid/plan-tagline.tsx +1 -1
  240. package/src/components/features-grid/style.scss +107 -19
  241. package/src/components/features-grid/table.tsx +4 -2
  242. package/src/components/features.tsx +66 -6
  243. package/src/components/item.tsx +6 -3
  244. package/src/components/plan-button/index.tsx +7 -1
  245. package/src/components/plan-button/style.scss +75 -51
  246. package/src/components/plan-div-td-container.tsx +6 -2
  247. package/src/components/plan-logo.tsx +16 -9
  248. package/src/components/plan-type-selector/components/interval-type-dropdown.tsx +14 -1
  249. package/src/components/plan-type-selector/hooks/use-max-discount.ts +8 -47
  250. package/src/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.ts +19 -17
  251. package/src/components/plans-2023-tooltip.tsx +17 -5
  252. package/src/components/shared/action-button/index.tsx +46 -5
  253. package/src/components/shared/action-button/style.scss +4 -0
  254. package/src/components/shared/billing-timeframe/index.tsx +12 -7
  255. package/src/components/shared/header-price/index.tsx +129 -27
  256. package/src/components/shared/header-price/style.scss +9 -3
  257. package/src/components/shared/storage/components/plan-storage.tsx +2 -2
  258. package/src/components/shared/storage/components/storage-dropdown.tsx +36 -15
  259. package/src/components/shared/storage/components/storage-feature-label.tsx +2 -1
  260. package/src/components/shared/storage/hooks/use-plan-storage.ts +3 -0
  261. package/src/components/test/actions-button.tsx +5 -0
  262. package/src/components/test/billing-timeframe.tsx +1 -1
  263. package/src/components/test/header-price.tsx +342 -4
  264. package/src/fixtures/sites-purchases.ts +2 -4
  265. package/src/grid-context.tsx +9 -0
  266. package/src/hooks/data-store/get-renewal-pricing-text.ts +73 -0
  267. package/src/hooks/data-store/types.ts +21 -0
  268. package/src/hooks/data-store/use-grid-plans-for-comparison-grid.ts +10 -0
  269. package/src/hooks/data-store/use-grid-plans-for-features-grid.ts +10 -0
  270. package/src/hooks/data-store/use-grid-plans.tsx +189 -23
  271. package/src/hooks/data-store/use-highlight-labels.ts +12 -3
  272. package/src/hooks/data-store/use-plan-billing-description.tsx +80 -15
  273. package/src/hooks/data-store/use-plan-features-for-grid-plans.ts +135 -1
  274. package/src/hooks/data-store/use-restructured-plan-features-for-comparison-grid.ts +93 -20
  275. package/src/hooks/data-store/use-title-badges.ts +31 -0
  276. package/src/hooks/test/use-visible-grid-plans.tsx +116 -0
  277. package/src/hooks/use-is-large-currency.ts +1 -1
  278. package/src/hooks/use-visible-grid-plans.tsx +102 -0
  279. package/src/index.tsx +18 -0
  280. package/src/lib/get-plan-features-object.ts +23 -2
  281. package/src/lib/plan-pricing-utils.ts +211 -0
  282. package/src/lib/test/plan-pricing-utils.ts +594 -0
  283. package/src/style-imports.d.ts +3 -0
  284. package/src/types.ts +41 -0
  285. package/dist/cjs/components/features-grid/mobile-free-domain.js +0 -25
  286. package/dist/cjs/components/features-grid/mobile-free-domain.js.map +0 -1
  287. package/dist/cjs/lib/get-plan-pricing-info-from-grid-plans.js +0 -15
  288. package/dist/cjs/lib/get-plan-pricing-info-from-grid-plans.js.map +0 -1
  289. package/dist/esm/components/features-grid/mobile-free-domain.js +0 -23
  290. package/dist/esm/components/features-grid/mobile-free-domain.js.map +0 -1
  291. package/dist/esm/lib/get-plan-pricing-info-from-grid-plans.js +0 -12
  292. package/dist/esm/lib/get-plan-pricing-info-from-grid-plans.js.map +0 -1
  293. package/dist/types/components/features-grid/mobile-free-domain.d.ts +0 -8
  294. package/dist/types/components/features-grid/mobile-free-domain.d.ts.map +0 -1
  295. package/dist/types/lib/get-plan-pricing-info-from-grid-plans.d.ts +0 -9
  296. package/dist/types/lib/get-plan-pricing-info-from-grid-plans.d.ts.map +0 -1
  297. package/src/components/features-grid/mobile-free-domain.tsx +0 -51
  298. package/src/lib/get-plan-pricing-info-from-grid-plans.ts +0 -31
@@ -3,9 +3,12 @@ import {
3
3
  FEATURE_GROUP_ESSENTIAL_FEATURES,
4
4
  FEATURE_GROUP_PAYMENT_TRANSACTION_FEES,
5
5
  getPlans,
6
+ FEATURE_AI_WRITER_DESIGNER,
7
+ FEATURE_AI_WRITER_DESIGNER_LIMITED,
8
+ FEATURE_REALTIME_BACKUPS_JP,
6
9
  } from '@automattic/calypso-products';
7
10
  import { Gridicon, JetpackLogo } from '@automattic/components';
8
- import { AddOns, Plans } from '@automattic/data-stores';
11
+ import { AddOns } from '@automattic/data-stores';
9
12
  import { css } from '@emotion/react';
10
13
  import styled from '@emotion/styled';
11
14
  import { useRef, useMemo } from '@wordpress/element';
@@ -24,10 +27,10 @@ import {
24
27
  import { useInView } from 'react-intersection-observer';
25
28
  import { plansGridMediumLarge } from '../../css-mixins';
26
29
  import PlansGridContextProvider, { usePlansGridContext } from '../../grid-context';
27
- import usePlanBillingPeriod from '../../hooks/data-store/use-plan-billing-period';
28
30
  import useGridSize from '../../hooks/use-grid-size';
29
31
  import useHighlightAdjacencyMatrix from '../../hooks/use-highlight-adjacency-matrix';
30
32
  import { useManageTooltipToggle } from '../../hooks/use-manage-tooltip-toggle';
33
+ import { useVisibleGridPlans } from '../../hooks/use-visible-grid-plans';
31
34
  import filterUnusedFeaturesObject from '../../lib/filter-unused-features-object';
32
35
  import getPlanFeaturesObject from '../../lib/get-plan-features-object';
33
36
  import PlanTypeSelector from '../plan-type-selector';
@@ -57,16 +60,37 @@ import type {
57
60
  } from '@automattic/calypso-products';
58
61
  import './style.scss';
59
62
 
63
+ // Plans Differentiators Experiment: treat feature variants (e.g., _LIMITED) as the same row
64
+ const FEATURE_ALIASES: Record< string, string[] > = {
65
+ [ FEATURE_AI_WRITER_DESIGNER ]: [ FEATURE_AI_WRITER_DESIGNER_LIMITED ],
66
+ };
67
+
68
+ // Finds a matching feature, checking both the base slug and any aliases
69
+ const findFeatureWithAlias = (
70
+ featureSlug: string | undefined,
71
+ planFeatures: { getSlug: () => string }[]
72
+ ) => {
73
+ if ( ! featureSlug ) {
74
+ return undefined;
75
+ }
76
+ const slugsToCheck = [ featureSlug, ...( FEATURE_ALIASES[ featureSlug ] ?? [] ) ];
77
+ return planFeatures.find( ( f ) => slugsToCheck.includes( f.getSlug() ) );
78
+ };
79
+
60
80
  const featureGroupRowTitleCellMaxWidth = 450;
61
81
  const rowCellMaxWidth = 290;
62
82
 
63
83
  const JetpackIconContainer = styled.div`
64
- padding-inline-start: 6px;
84
+ padding-inline-start: 3px;
65
85
  display: inline-block;
66
86
  vertical-align: middle;
67
87
  line-height: 1;
68
88
  `;
69
89
 
90
+ const TitlePreventOrphans = styled.span`
91
+ white-space: nowrap;
92
+ `;
93
+
70
94
  const Title = styled.div< { isHiddenInMobile?: boolean } >`
71
95
  font-weight: 500;
72
96
  font-size: 20px;
@@ -97,11 +121,10 @@ const Title = styled.div< { isHiddenInMobile?: boolean } >`
97
121
  ` ) }
98
122
  `;
99
123
 
100
- const Grid = styled.div< { visiblePlans: number } >`
124
+ const StickyGrid = styled( StickyContainer )< { visiblePlans: number } >`
101
125
  display: grid;
102
126
  margin: 0 auto;
103
127
  background: #fff;
104
- border: solid 1px #e0e0e0;
105
128
  ${ ( props ) =>
106
129
  props.visiblePlans &&
107
130
  css`
@@ -111,11 +134,21 @@ const Grid = styled.div< { visiblePlans: number } >`
111
134
  ${ plansGridMediumLarge( css`
112
135
  border-radius: 5px;
113
136
  ` ) }
137
+ `;
114
138
 
115
- > .is-sticky-header-row {
116
- border-bottom: solid 1px #e0e0e0;
117
- background: #fff;
118
- }
139
+ const Grid = styled.div< { visiblePlans: number; as?: string } >`
140
+ display: ${ ( props ) => ( props.as === 'tbody' ? 'table-row-group' : 'grid' ) };
141
+ margin: 0 auto;
142
+ background: #fff;
143
+ ${ ( props ) =>
144
+ props.visiblePlans &&
145
+ css`
146
+ max-width: ${ rowCellMaxWidth * props.visiblePlans + featureGroupRowTitleCellMaxWidth }px;
147
+ ` }
148
+
149
+ ${ plansGridMediumLarge( css`
150
+ border-radius: 5px;
151
+ ` ) }
119
152
  `;
120
153
 
121
154
  const Row = styled.div< {
@@ -219,7 +252,7 @@ const Cell = styled.div< { textAlign?: 'start' | 'center' | 'end' } >`
219
252
  border-right: none;
220
253
  justify-content: center;
221
254
 
222
- &:first-of-type {
255
+ &:first-of-type:not( .popular-plan-parent-class ) {
223
256
  padding-inline-start: 0;
224
257
  }
225
258
  &:last-of-type {
@@ -233,7 +266,27 @@ const Cell = styled.div< { textAlign?: 'start' | 'center' | 'end' } >`
233
266
  ` ) }
234
267
  `;
235
268
 
236
- const RowTitleCell = styled.div< {
269
+ const RowTitleCell = styled.td< {
270
+ isPlaceholderHeaderCell?: boolean;
271
+ isFeatureGroupRowTitleCell?: boolean;
272
+ } >`
273
+ display: none;
274
+ font-size: 14px;
275
+ padding-right: 10px;
276
+ ${ plansGridMediumLarge( css`
277
+ display: block;
278
+ flex: 1;
279
+ min-width: 290px;
280
+ ` ) }
281
+ max-width: ${ ( props ) => {
282
+ if ( props.isPlaceholderHeaderCell || props.isFeatureGroupRowTitleCell ) {
283
+ return `${ featureGroupRowTitleCellMaxWidth }px`;
284
+ }
285
+ return `${ rowCellMaxWidth }px`;
286
+ } };
287
+ `;
288
+
289
+ const RowHeaderCell = styled.th< {
237
290
  isPlaceholderHeaderCell?: boolean;
238
291
  isFeatureGroupRowTitleCell?: boolean;
239
292
  } >`
@@ -362,7 +415,7 @@ const ComparisonGridHeaderCell = ( {
362
415
  showRefundPeriod,
363
416
  isStuck,
364
417
  }: ComparisonGridHeaderCellProps ) => {
365
- const { gridPlansIndex } = usePlansGridContext();
418
+ const { gridPlansIndex, showBillingDescriptionForIncreasedRenewalPrice } = usePlansGridContext();
366
419
  const gridPlan = gridPlansIndex[ planSlug ];
367
420
  const highlightAdjacencyMatrix = useHighlightAdjacencyMatrix( {
368
421
  renderedGridPlans: visibleGridPlans,
@@ -389,7 +442,13 @@ const ComparisonGridHeaderCell = ( {
389
442
  const showPlanSelect = ! allVisible && ! gridPlan.current;
390
443
 
391
444
  return (
392
- <Cell className={ headerClasses } textAlign="start">
445
+ <Cell
446
+ as="th"
447
+ className={ headerClasses }
448
+ textAlign="start"
449
+ { ...{ scope: 'col' } }
450
+ aria-label={ gridPlan.planTitle as string }
451
+ >
393
452
  <PopularBadge
394
453
  isInSignup={ isInSignup }
395
454
  planSlug={ planSlug }
@@ -449,6 +508,7 @@ const ComparisonGridHeaderCell = ( {
449
508
  showMonthlyPrice={ false }
450
509
  isStuck={ false }
451
510
  visibleGridPlans={ visibleGridPlans }
511
+ showPostButtonText={ showBillingDescriptionForIncreasedRenewalPrice ? false : true }
452
512
  />
453
513
  </Cell>
454
514
  );
@@ -484,7 +544,7 @@ const ComparisonGridHeader = forwardRef< HTMLDivElement, ComparisonGridHeaderPro
484
544
  const { coupon } = usePlansGridContext();
485
545
 
486
546
  return (
487
- <PlanRow isHiddenInMobile={ isHiddenInMobile } ref={ ref }>
547
+ <PlanRow as="tr" isHiddenInMobile={ isHiddenInMobile } ref={ ref }>
488
548
  <RowTitleCell
489
549
  key="feature-name"
490
550
  className="plan-comparison-grid__header-cell is-placeholder-header-cell"
@@ -525,6 +585,7 @@ const ComparisonGridHeader = forwardRef< HTMLDivElement, ComparisonGridHeaderPro
525
585
  );
526
586
  }
527
587
  );
588
+ ComparisonGridHeader.displayName = 'ComparisonGridHeader';
528
589
 
529
590
  const ComparisonGridFeatureGroupRowCell: React.FunctionComponent< {
530
591
  feature?: FeatureObject;
@@ -560,19 +621,23 @@ const ComparisonGridFeatureGroupRowCell: React.FunctionComponent< {
560
621
  }
561
622
 
562
623
  const featureSlug = feature?.getSlug();
624
+ const comparisonGridTitle =
625
+ featureSlug === FEATURE_REALTIME_BACKUPS_JP
626
+ ? translate( 'Real-time backups', { textOnly: true } )
627
+ : feature?.getAlternativeTitle?.() || feature?.getTitle();
628
+
629
+ const planFeatures = [
630
+ ...gridPlan.features.wpcomFeatures,
631
+ ...gridPlan.features.jetpackFeatures,
632
+ ].filter( ( feature ) =>
633
+ 'monthly' === intervalType ? ! feature.availableOnlyForAnnualPlans : true
634
+ );
563
635
 
564
- const hasFeature =
565
- isStorageFeature ||
566
- ( featureSlug
567
- ? [ ...gridPlan.features.wpcomFeatures, ...gridPlan.features.jetpackFeatures ]
568
- .filter( ( feature ) =>
569
- 'monthly' === intervalType ? ! feature.availableOnlyForAnnualPlans : true
570
- )
571
- .some( ( feature ) => feature.getSlug() === featureSlug )
572
- : false );
573
-
574
- const featureLabel = featureSlug
575
- ? gridPlan?.features?.comparisonGridFeatureLabels?.[ featureSlug ]
636
+ const matchingFeature = findFeatureWithAlias( featureSlug, planFeatures );
637
+ const hasFeature = isStorageFeature || !! matchingFeature;
638
+ const featureLabelSlug = matchingFeature?.getSlug() ?? featureSlug;
639
+ const featureLabel = featureLabelSlug
640
+ ? gridPlan?.features?.comparisonGridFeatureLabels?.[ featureLabelSlug ]
576
641
  : undefined;
577
642
 
578
643
  const cellClasses = clsx(
@@ -595,7 +660,7 @@ const ComparisonGridFeatureGroupRowCell: React.FunctionComponent< {
595
660
  );
596
661
 
597
662
  return (
598
- <Cell className={ cellClasses } textAlign="center">
663
+ <Cell as="td" className={ cellClasses } textAlign="center">
599
664
  { isStorageFeature ? (
600
665
  <>
601
666
  <span className="plan-comparison-grid__plan-title">{ translate( 'Storage' ) }</span>
@@ -618,7 +683,7 @@ const ComparisonGridFeatureGroupRowCell: React.FunctionComponent< {
618
683
  id={ `${ planSlug }-${ featureSlug }` }
619
684
  >
620
685
  <span className="plan-comparison-grid__plan-title">
621
- { feature?.getAlternativeTitle?.() || feature?.getTitle() }
686
+ { comparisonGridTitle }
622
687
  </span>
623
688
  </Plans2023Tooltip>
624
689
  <span className="plan-comparison-grid__plan-conditional-title">
@@ -643,9 +708,7 @@ const ComparisonGridFeatureGroupRowCell: React.FunctionComponent< {
643
708
  activeTooltipId={ activeTooltipId }
644
709
  id={ `${ planSlug }-${ featureSlug }` }
645
710
  >
646
- <span className="plan-comparison-grid__plan-title">
647
- { feature?.getAlternativeTitle?.() || feature?.getTitle() }
648
- </span>
711
+ <span className="plan-comparison-grid__plan-title">{ comparisonGridTitle }</span>
649
712
  </Plans2023Tooltip>
650
713
  { feature?.getCompareTitle && (
651
714
  <span className="plan-comparison-grid__plan-subtitle">
@@ -663,9 +726,19 @@ const ComparisonGridFeatureGroupRowCell: React.FunctionComponent< {
663
726
  </span>
664
727
  ) }
665
728
  { hasFeature && ! featureLabel && (
666
- <Gridicon icon="checkmark" color="var(--studio-wordpress-blue-50)" />
729
+ <Gridicon
730
+ icon="checkmark"
731
+ color="var(--studio-wordpress-blue-50)"
732
+ aria-label={ translate( 'Feature available' ) }
733
+ />
734
+ ) }
735
+ { ! hasFeature && ! featureLabel && (
736
+ <Gridicon
737
+ icon="minus-small"
738
+ color="#C3C4C7"
739
+ aria-label={ translate( 'Feature not available' ) }
740
+ />
667
741
  ) }
668
- { ! hasFeature && ! featureLabel && <Gridicon icon="minus-small" color="#C3C4C7" /> }
669
742
  </>
670
743
  ) }
671
744
  </>
@@ -676,6 +749,7 @@ const ComparisonGridFeatureGroupRowCell: React.FunctionComponent< {
676
749
 
677
750
  const ComparisonGridFeatureGroupRow: React.FunctionComponent< {
678
751
  feature?: FeatureObject | TransformedFeatureObject;
752
+ featureGroupSlug: string;
679
753
  isHiddenInMobile: boolean;
680
754
  allJetpackFeatures: Set< string >;
681
755
  visibleGridPlans: GridPlan[];
@@ -689,6 +763,7 @@ const ComparisonGridFeatureGroupRow: React.FunctionComponent< {
689
763
  onStorageAddOnClick?: ( addOnSlug: AddOns.StorageAddOnSlug ) => void;
690
764
  } > = ( {
691
765
  feature,
766
+ featureGroupSlug,
692
767
  isHiddenInMobile,
693
768
  allJetpackFeatures,
694
769
  visibleGridPlans,
@@ -707,20 +782,29 @@ const ComparisonGridFeatureGroupRow: React.FunctionComponent< {
707
782
  } );
708
783
  const featureSlug = feature?.getSlug() ?? '';
709
784
  const footnote = planFeatureFootnotes?.footnotesByFeature?.[ featureSlug ];
710
- const tooltipId = `${ feature?.getSlug() }-comparison-grid`;
785
+ const tooltipId = `${ featureGroupSlug }-${ feature?.getSlug() }-comparison-grid`;
786
+ const title =
787
+ featureSlug === FEATURE_REALTIME_BACKUPS_JP
788
+ ? // Always display the short title for backups in comparison grid.
789
+ translate( 'Real-time backups', { textOnly: true } )
790
+ : feature?.getTitle?.();
791
+ const headerAriaLabel: string = typeof title === 'string' ? title : '';
711
792
 
712
793
  const { enableFeatureTooltips } = usePlansGridContext();
713
794
 
714
795
  return (
715
796
  <Row
797
+ as="tr"
716
798
  isHiddenInMobile={ isHiddenInMobile }
717
799
  className={ rowClasses }
718
800
  isHighlighted={ isHighlighted }
719
801
  >
720
- <RowTitleCell
802
+ <RowHeaderCell
721
803
  key="feature-name"
722
804
  className="is-feature-group-row-title-cell"
723
805
  isFeatureGroupRowTitleCell
806
+ scope="row"
807
+ aria-label={ headerAriaLabel }
724
808
  >
725
809
  { isStorageFeature ? (
726
810
  <Plans2023Tooltip
@@ -745,32 +829,52 @@ const ComparisonGridFeatureGroupRow: React.FunctionComponent< {
745
829
  activeTooltipId={ activeTooltipId }
746
830
  id={ tooltipId }
747
831
  >
748
- { feature.getTitle() }
749
- { footnote && (
750
- <FeatureFootnote>
751
- <sup>{ footnote }</sup>
752
- </FeatureFootnote>
832
+ { typeof title === 'string' ? (
833
+ <>
834
+ { title.split( ' ' ).slice( 0, -1 ).join( ' ' ) }
835
+ { title.includes( ' ' ) ? ' ' : null }
836
+ <TitlePreventOrphans>
837
+ { title.split( ' ' ).slice( -1 ) }
838
+ { footnote && (
839
+ <FeatureFootnote>
840
+ <sup>{ footnote }</sup>
841
+ </FeatureFootnote>
842
+ ) }
843
+ { allJetpackFeatures.has( feature.getSlug() ) ? (
844
+ <>
845
+ { '\u00A0' }
846
+ <JetpackIconContainer>
847
+ <Plans2023Tooltip
848
+ text={ translate(
849
+ 'Security, performance, and growth tools—powered by Jetpack.'
850
+ ) }
851
+ setActiveTooltipId={ setActiveTooltipId }
852
+ activeTooltipId={ activeTooltipId }
853
+ id={ `jp-${ tooltipId }` }
854
+ >
855
+ <JetpackLogo size={ 16 } />
856
+ </Plans2023Tooltip>
857
+ </JetpackIconContainer>
858
+ </>
859
+ ) : null }
860
+ </TitlePreventOrphans>
861
+ </>
862
+ ) : (
863
+ <>
864
+ { feature.getTitle() }
865
+ { footnote && (
866
+ <FeatureFootnote>
867
+ <sup>{ footnote }</sup>
868
+ </FeatureFootnote>
869
+ ) }
870
+ </>
753
871
  ) }
754
872
  </Plans2023Tooltip>
755
- { allJetpackFeatures.has( feature.getSlug() ) ? (
756
- <JetpackIconContainer>
757
- <Plans2023Tooltip
758
- text={ translate(
759
- 'Security, performance, and growth tools—powered by Jetpack.'
760
- ) }
761
- setActiveTooltipId={ setActiveTooltipId }
762
- activeTooltipId={ activeTooltipId }
763
- id={ `jp-${ tooltipId }` }
764
- >
765
- <JetpackLogo size={ 16 } />
766
- </Plans2023Tooltip>
767
- </JetpackIconContainer>
768
- ) : null }
769
873
  </>
770
874
  ) }
771
875
  </>
772
876
  ) }
773
- </RowTitleCell>
877
+ </RowHeaderCell>
774
878
  { visibleGridPlans.map( ( { planSlug } ) => (
775
879
  <ComparisonGridFeatureGroupRowCell
776
880
  key={ planSlug }
@@ -801,6 +905,7 @@ const FeatureGroup = ( {
801
905
  featureGroupMap,
802
906
  visibleGridPlans,
803
907
  planFeatureFootnotes,
908
+ plansLength,
804
909
  }: {
805
910
  featureGroup: FeatureGroup;
806
911
  selectedFeature?: string;
@@ -815,17 +920,20 @@ const FeatureGroup = ( {
815
920
  footnoteList: string[];
816
921
  footnotesByFeature: Record< string, number >;
817
922
  };
923
+ plansLength: number;
818
924
  } ) => {
819
- const { allFeaturesList } = usePlansGridContext();
925
+ const { allFeaturesList, isExperimentVariant } = usePlansGridContext();
820
926
  const [ firstSetOfFeatures ] = Object.keys( featureGroupMap );
821
927
  const [ visibleFeatureGroups, setVisibleFeatureGroups ] = useState< string[] >( [
822
928
  firstSetOfFeatures,
823
929
  ] );
824
930
  const features = featureGroup.getFeatures();
931
+
825
932
  const featureObjects = filterUnusedFeaturesObject(
826
933
  visibleGridPlans,
827
- getPlanFeaturesObject( allFeaturesList, features )
934
+ getPlanFeaturesObject( allFeaturesList, features, isExperimentVariant )
828
935
  );
936
+
829
937
  const isHiddenInMobile = ! visibleFeatureGroups.includes( featureGroup.slug );
830
938
 
831
939
  const allJetpackFeatures = useMemo( () => {
@@ -871,12 +979,18 @@ const FeatureGroup = ( {
871
979
  }
872
980
 
873
981
  return (
874
- <div key={ featureGroup.slug } className="plan-comparison-grid__feature-group">
982
+ <Grid
983
+ as="tbody"
984
+ visiblePlans={ plansLength }
985
+ key={ featureGroup.slug }
986
+ className="plan-comparison-grid__feature-group"
987
+ >
875
988
  <TitleRow
989
+ as="tr"
876
990
  className="plan-comparison-grid__feature-group-title-row"
877
991
  onClick={ handleFeatureGroupToggle }
878
992
  >
879
- <Title isHiddenInMobile={ isHiddenInMobile }>
993
+ <Title as="td" isHiddenInMobile={ isHiddenInMobile }>
880
994
  <Gridicon icon="chevron-up" size={ 12 } color="#1E1E1E" />
881
995
  <span>{ featureGroup.getTitle() }</span>
882
996
  </Title>
@@ -885,6 +999,7 @@ const FeatureGroup = ( {
885
999
  <ComparisonGridFeatureGroupRow
886
1000
  key={ feature.getSlug() }
887
1001
  feature={ feature }
1002
+ featureGroupSlug={ featureGroup.slug }
888
1003
  isHiddenInMobile={ isHiddenInMobile }
889
1004
  allJetpackFeatures={ allJetpackFeatures }
890
1005
  visibleGridPlans={ visibleGridPlans }
@@ -901,6 +1016,7 @@ const FeatureGroup = ( {
901
1016
  { featureGroup.slug === FEATURE_GROUP_ESSENTIAL_FEATURES ? (
902
1017
  <ComparisonGridFeatureGroupRow
903
1018
  key="feature-storage"
1019
+ featureGroupSlug={ featureGroup.slug }
904
1020
  isHiddenInMobile={ isHiddenInMobile }
905
1021
  allJetpackFeatures={ allJetpackFeatures }
906
1022
  visibleGridPlans={ visibleGridPlans }
@@ -914,7 +1030,7 @@ const FeatureGroup = ( {
914
1030
  onStorageAddOnClick={ onStorageAddOnClick }
915
1031
  />
916
1032
  ) : null }
917
- </div>
1033
+ </Grid>
918
1034
  );
919
1035
  };
920
1036
 
@@ -932,88 +1048,21 @@ const ComparisonGrid = ( {
932
1048
  planTypeSelectorProps,
933
1049
  gridSize,
934
1050
  siteId,
1051
+ onVisiblePlansCountChange,
935
1052
  }: ComparisonGridProps ) => {
936
- const { gridPlans, gridPlansIndex, featureGroupMap } = usePlansGridContext();
1053
+ const { gridPlans, featureGroupMap } = usePlansGridContext();
937
1054
  const [ activeTooltipId, setActiveTooltipId ] = useManageTooltipToggle();
938
- const [ visiblePlans, setVisiblePlans ] = useState< PlanSlug[] >( [] );
939
- const currentPlanTerm = Plans.useCurrentPlanTerm( { siteId } );
940
- const selectedPlanTerm = usePlanBillingPeriod( { intervalType } );
941
-
942
- useEffect( () => {
943
- let numPlansToDisplay = gridPlans.length;
944
-
945
- switch ( gridSize ) {
946
- case 'large':
947
- numPlansToDisplay = 4;
948
- break;
949
- case 'medium':
950
- numPlansToDisplay = 3;
951
- break;
952
- case 'smedium':
953
- case 'small':
954
- numPlansToDisplay = 2;
955
- break;
956
- }
957
1055
 
958
- let visiblePlanSlugs = gridPlans
959
- .slice( 0, numPlansToDisplay )
960
- .map( ( { planSlug } ) => planSlug );
961
-
962
- const isCurrentPlanVisible =
963
- !! currentSitePlanSlug && visiblePlanSlugs.includes( currentSitePlanSlug );
964
-
965
- /**
966
- * Plans are sorted by least to most expensive unless:
967
- * - a current plan exists and
968
- * - the current plan's term matches the selected term and
969
- * - the current plan would not be displayed due to the number of plans that can be visible at once
970
- *
971
- * If those conditions are met:
972
- * - the current plan is placed at the start of the grid and
973
- * - the last plan is removed to maintain the expected number of visible plans
974
- */
975
- if ( currentSitePlanSlug && ! isCurrentPlanVisible && currentPlanTerm === selectedPlanTerm ) {
976
- visiblePlanSlugs = [ currentSitePlanSlug, ...visiblePlanSlugs ].slice( 0, numPlansToDisplay );
977
- }
978
-
979
- setVisiblePlans( visiblePlanSlugs );
980
- }, [
1056
+ const { visibleGridPlans, setVisibleGridPlans } = useVisibleGridPlans( {
981
1057
  gridSize,
982
- gridPlansIndex,
983
1058
  currentSitePlanSlug,
984
- gridPlans,
985
- currentPlanTerm,
986
- selectedPlanTerm,
1059
+ siteId,
987
1060
  intervalType,
988
- ] );
989
-
990
- const visibleGridPlans = useMemo(
991
- () =>
992
- visiblePlans.reduce( ( acc, planSlug ) => {
993
- const gridPlan = gridPlans.find(
994
- ( gridPlan ) => getPlanClass( gridPlan.planSlug ) === getPlanClass( planSlug )
995
- );
996
-
997
- if ( gridPlan ) {
998
- acc.push( gridPlan );
999
- }
1000
-
1001
- return acc;
1002
- }, [] as GridPlan[] ),
1003
- [ visiblePlans, gridPlans ]
1004
- );
1005
-
1006
- const onPlanChange = useCallback(
1007
- ( currentPlan: PlanSlug, event: ChangeEvent< HTMLSelectElement > ) => {
1008
- const newPlan = event.currentTarget.value;
1009
- const newVisiblePlans = visiblePlans.map( ( plan ) =>
1010
- plan === currentPlan ? ( newPlan as PlanSlug ) : plan
1011
- );
1061
+ } );
1012
1062
 
1013
- setVisiblePlans( newVisiblePlans );
1014
- },
1015
- [ visiblePlans ]
1016
- );
1063
+ useEffect( () => {
1064
+ onVisiblePlansCountChange?.( visibleGridPlans.length );
1065
+ }, [ visibleGridPlans.length, onVisiblePlansCountChange ] );
1017
1066
 
1018
1067
  const planFeatureFootnotes = useMemo( () => {
1019
1068
  // This is the main list of all footnotes. It is displayed at the bottom of the comparison grid.
@@ -1048,6 +1097,20 @@ const ComparisonGrid = ( {
1048
1097
  };
1049
1098
  }, [ featureGroupMap ] );
1050
1099
 
1100
+ const onPlanChange = useCallback(
1101
+ ( currentPlan: PlanSlug, event: ChangeEvent< HTMLSelectElement > ) => {
1102
+ const newPlanSlug = event.currentTarget.value;
1103
+ const newPlan = gridPlans.find( ( plan ) => plan.planSlug === newPlanSlug );
1104
+
1105
+ if ( newPlan ) {
1106
+ setVisibleGridPlans( ( previousGridPlans ) =>
1107
+ previousGridPlans.map( ( plan ) => ( plan.planSlug === currentPlan ? newPlan : plan ) )
1108
+ );
1109
+ }
1110
+ },
1111
+ [ gridPlans, setVisibleGridPlans ]
1112
+ );
1113
+
1051
1114
  // 100px is the padding of the footer row
1052
1115
  const [ bottomHeaderRef, isBottomHeaderInView ] = useInView( { rootMargin: '-100px' } );
1053
1116
 
@@ -1062,44 +1125,47 @@ const ComparisonGrid = ( {
1062
1125
  } );
1063
1126
 
1064
1127
  return (
1065
- <div className={ classes }>
1066
- <Grid visiblePlans={ visiblePlans.length }>
1067
- <StickyContainer
1068
- disabled={ isBottomHeaderInView }
1069
- stickyClass="is-sticky-header-row"
1070
- stickyOffset={ stickyRowOffset }
1071
- zIndex={ 1 }
1072
- >
1073
- { ( isStuck: boolean ) => (
1074
- <ComparisonGridHeader
1075
- displayedGridPlans={ gridPlans }
1076
- visibleGridPlans={ visibleGridPlans }
1077
- isInSignup={ isInSignup }
1078
- onPlanChange={ onPlanChange }
1079
- currentSitePlanSlug={ currentSitePlanSlug }
1080
- planActionOverrides={ planActionOverrides }
1081
- selectedPlan={ selectedPlan }
1082
- showRefundPeriod={ showRefundPeriod }
1083
- isStuck={ isStuck }
1084
- planTypeSelectorProps={ planTypeSelectorProps }
1085
- />
1086
- ) }
1087
- </StickyContainer>
1088
- { Object.values( featureGroupMap ).map( ( featureGroup: FeatureGroup ) => (
1089
- <FeatureGroup
1090
- key={ featureGroup.slug }
1091
- featureGroup={ featureGroup }
1128
+ <table className={ classes }>
1129
+ <StickyGrid
1130
+ visiblePlans={ visibleGridPlans.length }
1131
+ element="thead"
1132
+ disabled={ isBottomHeaderInView }
1133
+ stickyClass="is-sticky-header-row"
1134
+ stickyOffset={ stickyRowOffset }
1135
+ zIndex={ 1 }
1136
+ >
1137
+ { ( isStuck: boolean ) => (
1138
+ <ComparisonGridHeader
1139
+ displayedGridPlans={ gridPlans }
1092
1140
  visibleGridPlans={ visibleGridPlans }
1093
- featureGroupMap={ featureGroupMap }
1094
- selectedFeature={ selectedFeature }
1095
- intervalType={ intervalType }
1096
- activeTooltipId={ activeTooltipId }
1097
- setActiveTooltipId={ setActiveTooltipId }
1098
- showUpgradeableStorage={ showUpgradeableStorage }
1099
- onStorageAddOnClick={ onStorageAddOnClick }
1100
- planFeatureFootnotes={ planFeatureFootnotes }
1141
+ isInSignup={ isInSignup }
1142
+ onPlanChange={ onPlanChange }
1143
+ currentSitePlanSlug={ currentSitePlanSlug }
1144
+ planActionOverrides={ planActionOverrides }
1145
+ selectedPlan={ selectedPlan }
1146
+ showRefundPeriod={ showRefundPeriod }
1147
+ isStuck={ isStuck }
1148
+ planTypeSelectorProps={ planTypeSelectorProps }
1101
1149
  />
1102
- ) ) }
1150
+ ) }
1151
+ </StickyGrid>
1152
+ { Object.values( featureGroupMap ).map( ( featureGroup: FeatureGroup ) => (
1153
+ <FeatureGroup
1154
+ key={ featureGroup.slug }
1155
+ featureGroup={ featureGroup }
1156
+ visibleGridPlans={ visibleGridPlans }
1157
+ featureGroupMap={ featureGroupMap }
1158
+ selectedFeature={ selectedFeature }
1159
+ intervalType={ intervalType }
1160
+ activeTooltipId={ activeTooltipId }
1161
+ setActiveTooltipId={ setActiveTooltipId }
1162
+ showUpgradeableStorage={ showUpgradeableStorage }
1163
+ onStorageAddOnClick={ onStorageAddOnClick }
1164
+ planFeatureFootnotes={ planFeatureFootnotes }
1165
+ plansLength={ visibleGridPlans.length }
1166
+ />
1167
+ ) ) }
1168
+ <tbody>
1103
1169
  <ComparisonGridHeader
1104
1170
  displayedGridPlans={ gridPlans }
1105
1171
  visibleGridPlans={ visibleGridPlans }
@@ -1115,20 +1181,22 @@ const ComparisonGrid = ( {
1115
1181
  ref={ bottomHeaderRef }
1116
1182
  planTypeSelectorProps={ planTypeSelectorProps }
1117
1183
  />
1118
- </Grid>
1184
+ </tbody>
1119
1185
 
1120
- <div className="plan-comparison-grid__footer">
1186
+ <tfoot className="plan-comparison-grid__footer">
1121
1187
  { planFeatureFootnotes?.footnoteList && (
1122
- <FeatureFootnotes>
1123
- <ol>
1124
- { planFeatureFootnotes?.footnoteList?.map( ( footnote, index ) => {
1125
- return <li key={ `${ footnote }-${ index }` }>{ footnote }</li>;
1126
- } ) }
1127
- </ol>
1188
+ <FeatureFootnotes as="tr">
1189
+ <td>
1190
+ <ol>
1191
+ { planFeatureFootnotes?.footnoteList?.map( ( footnote, index ) => {
1192
+ return <li key={ `${ footnote }-${ index }` }>{ footnote }</li>;
1193
+ } ) }
1194
+ </ol>
1195
+ </td>
1128
1196
  </FeatureFootnotes>
1129
1197
  ) }
1130
- </div>
1131
- </div>
1198
+ </tfoot>
1199
+ </table>
1132
1200
  );
1133
1201
  };
1134
1202
 
@@ -1159,6 +1227,9 @@ const WrappedComparisonGrid = ( {
1159
1227
  featureGroupMap,
1160
1228
  enableTermSavingsPriceDisplay,
1161
1229
  reflectStorageSelectionInPlanPrices,
1230
+ showSimplifiedBillingDescription,
1231
+ showBillingDescriptionForIncreasedRenewalPrice,
1232
+ isExperimentVariant,
1162
1233
  ...otherProps
1163
1234
  }: ComparisonGridExternalProps ) => {
1164
1235
  const gridContainerRef = useRef< HTMLDivElement >( null );
@@ -1194,6 +1265,7 @@ const WrappedComparisonGrid = ( {
1194
1265
  <div ref={ gridContainerRef } className={ classNames }>
1195
1266
  <PlansGridContextProvider
1196
1267
  intent={ intent }
1268
+ key={ intent }
1197
1269
  siteId={ siteId }
1198
1270
  gridPlans={ gridPlans }
1199
1271
  useCheckPlanAvailabilityForPurchase={ useCheckPlanAvailabilityForPurchase }
@@ -1206,6 +1278,11 @@ const WrappedComparisonGrid = ( {
1206
1278
  hideUnsupportedFeatures={ hideUnsupportedFeatures }
1207
1279
  enableTermSavingsPriceDisplay={ enableTermSavingsPriceDisplay }
1208
1280
  reflectStorageSelectionInPlanPrices={ reflectStorageSelectionInPlanPrices }
1281
+ showSimplifiedBillingDescription={ showSimplifiedBillingDescription }
1282
+ showBillingDescriptionForIncreasedRenewalPrice={
1283
+ showBillingDescriptionForIncreasedRenewalPrice
1284
+ }
1285
+ isExperimentVariant={ isExperimentVariant }
1209
1286
  >
1210
1287
  <ComparisonGrid
1211
1288
  intervalType={ intervalType }