@automattic/plans-grid-next 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/dist/cjs/__mocks__/wpcom-proxy-request.js +3 -4
  2. package/dist/cjs/__mocks__/wpcom-proxy-request.js.map +1 -1
  3. package/dist/cjs/_shared.scss +9 -4
  4. package/dist/cjs/components/comparison-grid/index.js +18 -29
  5. package/dist/cjs/components/comparison-grid/index.js.map +1 -1
  6. package/dist/cjs/components/comparison-grid/index.stories.js +1 -0
  7. package/dist/cjs/components/comparison-grid/index.stories.js.map +1 -1
  8. package/dist/cjs/components/comparison-grid/style.scss +0 -17
  9. package/dist/cjs/components/dropdown-option.js +1 -1
  10. package/dist/cjs/components/features-grid/index.js +21 -6
  11. package/dist/cjs/components/features-grid/index.js.map +1 -1
  12. package/dist/cjs/components/features-grid/index.stories.js +1 -0
  13. package/dist/cjs/components/features-grid/index.stories.js.map +1 -1
  14. package/dist/cjs/components/features-grid/style.scss +2 -19
  15. package/dist/cjs/components/item.js +1 -2
  16. package/dist/cjs/components/item.js.map +1 -1
  17. package/dist/cjs/components/plan-button/style.scss +8 -7
  18. package/dist/cjs/components/plan-type-selector/hooks/use-interval-options.js +1 -1
  19. package/dist/cjs/components/plan-type-selector/hooks/use-interval-options.js.map +1 -1
  20. package/dist/cjs/components/plan-type-selector/hooks/use-max-discount.js +1 -1
  21. package/dist/cjs/components/plan-type-selector/hooks/use-max-discount.js.map +1 -1
  22. package/dist/cjs/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.js +1 -1
  23. package/dist/cjs/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.js.map +1 -1
  24. package/dist/cjs/components/plan-type-selector/style.scss +3 -1
  25. package/dist/cjs/components/shared/billing-timeframe/index.js +13 -5
  26. package/dist/cjs/components/shared/billing-timeframe/index.js.map +1 -1
  27. package/dist/cjs/components/shared/header-price/style.scss +2 -2
  28. package/dist/cjs/components/shared/storage/hooks/use-default-storage-option.js +1 -1
  29. package/dist/cjs/components/shared/storage/hooks/use-default-storage-option.js.map +1 -1
  30. package/dist/cjs/components/shared/storage/hooks/use-purchased-storage-add-on.js +1 -1
  31. package/dist/cjs/components/shared/storage/hooks/use-purchased-storage-add-on.js.map +1 -1
  32. package/dist/cjs/components/shared/storage/index.js +3 -2
  33. package/dist/cjs/components/shared/storage/index.js.map +1 -1
  34. package/dist/cjs/components/sticky-container.js +66 -6
  35. package/dist/cjs/components/sticky-container.js.map +1 -1
  36. package/dist/cjs/hooks/data-store/is-popular-plan.js +1 -2
  37. package/dist/cjs/hooks/data-store/is-popular-plan.js.map +1 -1
  38. package/dist/cjs/hooks/data-store/use-grid-plans-for-features-grid.js +14 -11
  39. package/dist/cjs/hooks/data-store/use-grid-plans-for-features-grid.js.map +1 -1
  40. package/dist/cjs/hooks/data-store/use-plan-billing-description.js +2 -2
  41. package/dist/cjs/hooks/data-store/use-plan-billing-description.js.map +1 -1
  42. package/dist/cjs/hooks/use-grid-size.js +2 -4
  43. package/dist/cjs/hooks/use-grid-size.js.map +1 -1
  44. package/dist/cjs/hooks/use-is-large-currency.js +1 -1
  45. package/dist/cjs/hooks/use-is-large-currency.js.map +1 -1
  46. package/dist/cjs/hooks/use-manage-tooltip-toggle.js +1 -2
  47. package/dist/cjs/hooks/use-manage-tooltip-toggle.js.map +1 -1
  48. package/dist/cjs/hooks/use-plan-pricing-info-from-grid-plans.js +1 -2
  49. package/dist/cjs/hooks/use-plan-pricing-info-from-grid-plans.js.map +1 -1
  50. package/dist/cjs/lib/get-plan-pricing-info-from-grid-plans.js +1 -2
  51. package/dist/cjs/lib/get-plan-pricing-info-from-grid-plans.js.map +1 -1
  52. package/dist/cjs/lib/is-same-plan.js +1 -2
  53. package/dist/cjs/lib/is-same-plan.js.map +1 -1
  54. package/dist/cjs/lib/sort-plan-properties.js +19 -32
  55. package/dist/cjs/lib/sort-plan-properties.js.map +1 -1
  56. package/dist/cjs/lib/touch-detect/index.js +1 -2
  57. package/dist/cjs/lib/touch-detect/index.js.map +1 -1
  58. package/dist/esm/_shared.scss +9 -4
  59. package/dist/esm/components/comparison-grid/index.js +18 -29
  60. package/dist/esm/components/comparison-grid/index.js.map +1 -1
  61. package/dist/esm/components/comparison-grid/index.stories.js +1 -0
  62. package/dist/esm/components/comparison-grid/index.stories.js.map +1 -1
  63. package/dist/esm/components/comparison-grid/style.scss +0 -17
  64. package/dist/esm/components/dropdown-option.js +1 -1
  65. package/dist/esm/components/features-grid/index.js +21 -6
  66. package/dist/esm/components/features-grid/index.js.map +1 -1
  67. package/dist/esm/components/features-grid/index.stories.js +1 -0
  68. package/dist/esm/components/features-grid/index.stories.js.map +1 -1
  69. package/dist/esm/components/features-grid/style.scss +2 -19
  70. package/dist/esm/components/plan-button/style.scss +8 -7
  71. package/dist/esm/components/plan-type-selector/style.scss +3 -1
  72. package/dist/esm/components/shared/billing-timeframe/index.js +14 -6
  73. package/dist/esm/components/shared/billing-timeframe/index.js.map +1 -1
  74. package/dist/esm/components/shared/header-price/style.scss +2 -2
  75. package/dist/esm/components/sticky-container.js +66 -5
  76. package/dist/esm/components/sticky-container.js.map +1 -1
  77. package/dist/esm/hooks/data-store/use-grid-plans-for-features-grid.js +14 -11
  78. package/dist/esm/hooks/data-store/use-grid-plans-for-features-grid.js.map +1 -1
  79. package/dist/esm/hooks/data-store/use-plan-billing-description.js +1 -1
  80. package/dist/esm/hooks/use-grid-size.js +0 -1
  81. package/dist/esm/hooks/use-grid-size.js.map +1 -1
  82. package/dist/esm/lib/sort-plan-properties.js +18 -30
  83. package/dist/esm/lib/sort-plan-properties.js.map +1 -1
  84. package/dist/tsconfig-cjs.tsbuildinfo +1 -1
  85. package/dist/tsconfig.tsbuildinfo +1 -1
  86. package/dist/types/components/comparison-grid/index.d.ts +1 -1
  87. package/dist/types/components/comparison-grid/index.d.ts.map +1 -1
  88. package/dist/types/components/comparison-grid/index.stories.d.ts +3 -1
  89. package/dist/types/components/comparison-grid/index.stories.d.ts.map +1 -1
  90. package/dist/types/components/dropdown-option.d.ts +0 -1
  91. package/dist/types/components/dropdown-option.d.ts.map +1 -1
  92. package/dist/types/components/features-grid/client-logo-list/client-list.d.ts +0 -1
  93. package/dist/types/components/features-grid/client-logo-list/client-list.d.ts.map +1 -1
  94. package/dist/types/components/features-grid/client-logo-list/index.d.ts.map +1 -1
  95. package/dist/types/components/features-grid/index.d.ts.map +1 -1
  96. package/dist/types/components/features-grid/index.stories.d.ts +5 -1
  97. package/dist/types/components/features-grid/index.stories.d.ts.map +1 -1
  98. package/dist/types/components/plan-button/index.d.ts +10 -10
  99. package/dist/types/components/plan-button/index.d.ts.map +1 -1
  100. package/dist/types/components/plan-div-td-container.d.ts +0 -1
  101. package/dist/types/components/plan-div-td-container.d.ts.map +1 -1
  102. package/dist/types/components/plan-logo.d.ts +0 -1
  103. package/dist/types/components/plan-logo.d.ts.map +1 -1
  104. package/dist/types/components/plan-type-selector/components/interval-type-dropdown.d.ts +0 -1
  105. package/dist/types/components/plan-type-selector/components/interval-type-dropdown.d.ts.map +1 -1
  106. package/dist/types/components/plan-type-selector/components/interval-type-selector.d.ts +0 -1
  107. package/dist/types/components/plan-type-selector/components/interval-type-selector.d.ts.map +1 -1
  108. package/dist/types/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.d.ts.map +1 -1
  109. package/dist/types/components/popular-badge.d.ts +0 -1
  110. package/dist/types/components/popular-badge.d.ts.map +1 -1
  111. package/dist/types/components/shared/billing-timeframe/index.d.ts.map +1 -1
  112. package/dist/types/components/shared/header-price/header-price-context.d.ts +0 -1
  113. package/dist/types/components/shared/header-price/header-price-context.d.ts.map +1 -1
  114. package/dist/types/components/shared/storage/hooks/use-storage-string.d.ts +0 -1
  115. package/dist/types/components/shared/storage/hooks/use-storage-string.d.ts.map +1 -1
  116. package/dist/types/components/sticky-container.d.ts +22 -0
  117. package/dist/types/components/sticky-container.d.ts.map +1 -1
  118. package/dist/types/grid-context.d.ts +0 -1
  119. package/dist/types/grid-context.d.ts.map +1 -1
  120. package/dist/types/hooks/data-store/types.d.ts +1 -0
  121. package/dist/types/hooks/data-store/types.d.ts.map +1 -1
  122. package/dist/types/hooks/data-store/use-grid-plans-for-features-grid.d.ts +1 -1
  123. package/dist/types/hooks/data-store/use-grid-plans-for-features-grid.d.ts.map +1 -1
  124. package/dist/types/hooks/data-store/use-grid-plans.d.ts +1 -1
  125. package/dist/types/hooks/data-store/use-grid-plans.d.ts.map +1 -1
  126. package/dist/types/hooks/data-store/use-highlight-labels.d.ts +0 -1
  127. package/dist/types/hooks/data-store/use-highlight-labels.d.ts.map +1 -1
  128. package/dist/types/hooks/data-store/use-plan-billing-description.d.ts +0 -1
  129. package/dist/types/hooks/data-store/use-plan-billing-description.d.ts.map +1 -1
  130. package/dist/types/hooks/use-grid-size.d.ts +0 -1
  131. package/dist/types/hooks/use-grid-size.d.ts.map +1 -1
  132. package/dist/types/lib/get-plan-features-object.d.ts.map +1 -1
  133. package/dist/types/lib/sort-plan-properties.d.ts +1 -1
  134. package/dist/types/lib/sort-plan-properties.d.ts.map +1 -1
  135. package/dist/types/types.d.ts +1 -1
  136. package/dist/types/types.d.ts.map +1 -1
  137. package/package.json +14 -14
  138. package/src/_shared.scss +9 -4
  139. package/src/components/comparison-grid/index.stories.tsx +1 -0
  140. package/src/components/comparison-grid/index.tsx +19 -33
  141. package/src/components/comparison-grid/style.scss +0 -17
  142. package/src/components/dropdown-option.tsx +1 -1
  143. package/src/components/features-grid/index.stories.tsx +1 -0
  144. package/src/components/features-grid/index.tsx +21 -9
  145. package/src/components/features-grid/style.scss +2 -19
  146. package/src/components/plan-button/style.scss +8 -7
  147. package/src/components/plan-type-selector/style.scss +3 -1
  148. package/src/components/shared/billing-timeframe/index.tsx +15 -7
  149. package/src/components/shared/header-price/style.scss +2 -2
  150. package/src/components/sticky-container.tsx +74 -3
  151. package/src/components/test/billing-timeframe.tsx +2 -2
  152. package/src/hooks/data-store/types.ts +1 -0
  153. package/src/hooks/data-store/use-grid-plans-for-features-grid.ts +14 -10
  154. package/src/hooks/data-store/use-plan-billing-description.tsx +1 -1
  155. package/src/hooks/use-grid-size.ts +0 -1
  156. package/src/lib/sort-plan-properties.ts +18 -37
  157. package/src/lib/test/sort-plan-properties.ts +50 -32
  158. package/src/types.ts +1 -0
@@ -936,11 +936,11 @@ const ComparisonGrid = ( {
936
936
  const [ visiblePlans, setVisiblePlans ] = useState< PlanSlug[] >( [] );
937
937
 
938
938
  const displayedGridPlans = useMemo( () => {
939
- return sortPlans( gridPlans, currentSitePlanSlug, 'small' === gridSize );
940
- }, [ gridPlans, currentSitePlanSlug, gridSize ] );
939
+ return sortPlans( gridPlans, currentSitePlanSlug );
940
+ }, [ gridPlans, currentSitePlanSlug ] );
941
941
 
942
942
  useEffect( () => {
943
- setVisiblePlans( ( prev ) => {
943
+ setVisiblePlans( () => {
944
944
  let visibleLength = displayedGridPlans.length;
945
945
  switch ( gridSize ) {
946
946
  case 'large':
@@ -955,27 +955,7 @@ const ComparisonGrid = ( {
955
955
  break;
956
956
  }
957
957
 
958
- // visible length changed, update with the current gridPlans
959
- // - we don't care about previous order
960
- if ( prev.length !== visibleLength ) {
961
- return displayedGridPlans.slice( 0, visibleLength ).map( ( { planSlug } ) => planSlug );
962
- }
963
-
964
- // prev state out of sync with current gridPlans (e.g. gridPlans updated to a different term)
965
- // - we care about previous order
966
- const isPrevStale = prev.some( ( planSlug ) => ! gridPlansIndex[ planSlug ] );
967
- if ( isPrevStale ) {
968
- return prev.map( ( planSlug ) => {
969
- const gridPlan = displayedGridPlans.find(
970
- ( gridPlan ) => getPlanClass( gridPlan.planSlug ) === getPlanClass( planSlug )
971
- );
972
-
973
- return gridPlan?.planSlug ?? planSlug;
974
- } );
975
- }
976
-
977
- // nothing to update
978
- return prev;
958
+ return displayedGridPlans.slice( 0, visibleLength ).map( ( { planSlug } ) => planSlug );
979
959
  } );
980
960
  }, [ gridSize, displayedGridPlans, gridPlansIndex ] );
981
961
 
@@ -1124,14 +1104,6 @@ const ComparisonGrid = ( {
1124
1104
  );
1125
1105
  };
1126
1106
 
1127
- const GRID_BREAKPOINTS = new Map( [
1128
- [ 'small', 0 ],
1129
- [ 'smedium', 686 ],
1130
- [ 'medium', 835 ], // enough to fit Enterpreneur plan. was 686
1131
- [ 'large', 1005 ], // enough to fit Enterpreneur plan. was 870
1132
- [ 'xlarge', 1180 ],
1133
- ] );
1134
-
1135
1107
  // TODO
1136
1108
  // Now that everything under is functional component, we can deprecate this wrapper and only keep ComparisonGrid instead.
1137
1109
  // More details can be found in https://github.com/Automattic/wp-calypso/issues/87047
@@ -1144,6 +1116,7 @@ const WrappedComparisonGrid = ( {
1144
1116
  recordTracksEvent,
1145
1117
  allFeaturesList,
1146
1118
  intervalType,
1119
+ isInSiteDashboard,
1147
1120
  isInSignup,
1148
1121
  currentSitePlanSlug,
1149
1122
  selectedPlan,
@@ -1162,10 +1135,22 @@ const WrappedComparisonGrid = ( {
1162
1135
  }: ComparisonGridExternalProps ) => {
1163
1136
  const gridContainerRef = useRef< HTMLDivElement >( null );
1164
1137
 
1138
+ const gridBreakpoints = useMemo( () => {
1139
+ // we want to fit up to the Commerce plan in this breakpoint
1140
+ const xlargeBreakpoint = isInSiteDashboard ? 1114 : 1180;
1141
+ return new Map( [
1142
+ [ 'small', 0 ],
1143
+ [ 'smedium', 686 ],
1144
+ [ 'medium', 835 ],
1145
+ [ 'large', 1005 ],
1146
+ [ 'xlarge', xlargeBreakpoint ],
1147
+ ] );
1148
+ }, [ isInSiteDashboard ] );
1149
+
1165
1150
  // TODO: this will be deprecated along side removing the wrapper component
1166
1151
  const gridSize = useGridSize( {
1167
1152
  containerRef: gridContainerRef,
1168
- containerBreakpoints: GRID_BREAKPOINTS,
1153
+ containerBreakpoints: gridBreakpoints,
1169
1154
  } );
1170
1155
 
1171
1156
  const classNames = clsx( 'plans-grid-next', className, {
@@ -1196,6 +1181,7 @@ const WrappedComparisonGrid = ( {
1196
1181
  >
1197
1182
  <ComparisonGrid
1198
1183
  intervalType={ intervalType }
1184
+ isInSiteDashboard={ isInSiteDashboard }
1199
1185
  isInSignup={ isInSignup }
1200
1186
  currentSitePlanSlug={ currentSitePlanSlug }
1201
1187
  siteId={ siteId }
@@ -282,23 +282,6 @@
282
282
  @include plans-grid-medium-large {
283
283
  margin: 7px 0 10px;
284
284
  }
285
-
286
- .plans-grid-next__billing-timeframe-vip-price {
287
- font-size: $font-body;
288
- line-height: 24px;
289
- font-weight: 400;
290
- color: var(--studio-gray-80);
291
-
292
- @include plans-grid-medium-large {
293
- font-size: $font-body-small;
294
- line-height: 20px;
295
- }
296
-
297
- @include plans-grid-large {
298
- font-size: $font-body-extra-small;
299
- line-height: 16px;
300
- }
301
- }
302
285
  }
303
286
 
304
287
  .plans-grid-next-action-button {
@@ -16,7 +16,7 @@ const Container = styled.div`
16
16
  }
17
17
 
18
18
  span {
19
- color: var( --studio-green-50 );
19
+ color: var( --studio-green-60 );
20
20
  }
21
21
 
22
22
  .title {
@@ -68,6 +68,7 @@ const defaultProps = {
68
68
  hideUnavailableFeatures: false,
69
69
  isCustomDomainAllowedOnFreePlan: false,
70
70
  isInAdmin: false,
71
+ isInSiteDashboard: false,
71
72
  isInSignup: true,
72
73
  onStorageAddOnClick: () => {},
73
74
  planActionOverrides: undefined,
@@ -376,6 +376,7 @@ const WrappedFeaturesGrid = ( props: FeaturesGridExternalProps ) => {
376
376
  allFeaturesList,
377
377
  coupon,
378
378
  isInAdmin,
379
+ isInSiteDashboard,
379
380
  className,
380
381
  enableFeatureTooltips,
381
382
  enableCategorisedFeatures,
@@ -390,15 +391,26 @@ const WrappedFeaturesGrid = ( props: FeaturesGridExternalProps ) => {
390
391
 
391
392
  const gridContainerRef = useRef< HTMLDivElement >( null );
392
393
 
393
- const gridBreakpoints = useMemo(
394
- () =>
395
- new Map( [
396
- [ 'small', 0 ],
397
- [ 'medium', 740 ],
398
- [ 'large', isInAdmin ? 1180 : 1320 ], // 1320 to fit Enterpreneur plan, 1180 to work in admin
399
- ] ),
400
- [ isInAdmin ]
401
- );
394
+ const gridBreakpoints = useMemo( () => {
395
+ // we want to fit up to the Commerce plan in this breakpoint
396
+ let largeBreakpoint;
397
+ if ( isInSiteDashboard ) {
398
+ largeBreakpoint = 1042;
399
+ } else if ( isInAdmin ) {
400
+ largeBreakpoint = 1180;
401
+ } else {
402
+ largeBreakpoint = 1320;
403
+ }
404
+
405
+ // we want to fit 3 plans per row in this breakpoint
406
+ const mediumBreakpoint = isInSiteDashboard ? 667 : 741;
407
+
408
+ return new Map( [
409
+ [ 'small', 0 ],
410
+ [ 'medium', mediumBreakpoint ],
411
+ [ 'large', largeBreakpoint ],
412
+ ] );
413
+ }, [ isInAdmin, isInSiteDashboard ] );
402
414
 
403
415
  // TODO: this will be deprecated along side removing the wrapper component
404
416
  const gridSize = useGridSize( {
@@ -222,6 +222,7 @@
222
222
  line-height: 16px;
223
223
  padding-bottom: 8px;
224
224
  min-height: 50px;
225
+ text-wrap: balance;
225
226
  }
226
227
  }
227
228
 
@@ -349,6 +350,7 @@
349
350
  font-weight: 400;
350
351
  line-height: 20px;
351
352
  overflow-wrap: break-word;
353
+ text-wrap: balance;
352
354
  margin: 0;
353
355
  flex: 1 0 0;
354
356
  width: 100%;
@@ -441,18 +443,6 @@
441
443
  @include plans-grid-medium-large {
442
444
  padding-bottom: 0;
443
445
  }
444
-
445
- .plans-grid-next__billing-timeframe-vip-price {
446
- @include plans-grid-medium-large {
447
- font-size: $font-body-small;
448
- line-height: 20px;
449
- }
450
-
451
- @include plans-grid-large {
452
- font-size: $font-body-extra-small;
453
- line-height: 16px;
454
- }
455
- }
456
446
  }
457
447
 
458
448
  &.popular-plan-parent-class {
@@ -501,13 +491,6 @@
501
491
  font-weight: 400;
502
492
  font-size: $font-body-extra-small;
503
493
  padding: 0 19px 24px 20px;
504
-
505
- .plans-grid-next__billing-timeframe-vip-price {
506
- font-size: $font-body;
507
- line-height: 24px;
508
- font-weight: 400;
509
- color: var(--studio-gray-80);
510
- }
511
494
  }
512
495
 
513
496
  &.popular-plan-parent-class {
@@ -6,6 +6,7 @@
6
6
  padding: 10px 12px;
7
7
  border: unset;
8
8
  text-align: center;
9
+ text-wrap: balance;
9
10
  width: 100%;
10
11
  color: var(--color-text-inverted);
11
12
 
@@ -51,10 +52,10 @@
51
52
  }
52
53
 
53
54
  &.is-business-plan {
54
- background-color: var(--studio-woocommerce-purple-50);
55
+ background-color: var(--studio-woocommerce-purple-60);
55
56
 
56
57
  &:focus {
57
- box-shadow: 0 0 0 2px var(--studio-white), 0 0 0 4px var(--studio-woocommerce-purple-50);
58
+ box-shadow: 0 0 0 2px var(--studio-white), 0 0 0 4px var(--studio-woocommerce-purple-60);
58
59
  }
59
60
 
60
61
  &:hover {
@@ -64,11 +65,11 @@
64
65
  + .button.is-borderless {
65
66
  background: transparent;
66
67
  font-size: $font-body-small;
67
- color: var(--studio-woocommerce-purple-50);
68
+ color: var(--studio-woocommerce-purple-60);
68
69
  text-decoration: none;
69
70
 
70
71
  &:focus {
71
- box-shadow: 0 0 0 2px var(--studio-white), 0 0 0 4px var(--studio-woocommerce-purple-50);
72
+ box-shadow: 0 0 0 2px var(--studio-white), 0 0 0 4px var(--studio-woocommerce-purple-60);
72
73
  }
73
74
  }
74
75
  }
@@ -101,14 +102,14 @@
101
102
  }
102
103
 
103
104
  &.is-ecommerce-plan {
104
- background-color: var(--studio-woocommerce-purple-70);
105
+ background-color: var(--studio-woocommerce-purple-40);
105
106
 
106
107
  &:focus {
107
- box-shadow: 0 0 0 2px var(--studio-white), 0 0 0 4px var(--studio-woocommerce-purple-70);
108
+ box-shadow: 0 0 0 2px var(--studio-white), 0 0 0 4px var(--studio-woocommerce-purple-40);
108
109
  }
109
110
 
110
111
  &:hover {
111
- background-color: var(--studio-woocommerce-purple-80);
112
+ background-color: var(--studio-woocommerce-purple-50);
112
113
  }
113
114
  }
114
115
 
@@ -162,7 +162,9 @@
162
162
  display: none;
163
163
  }
164
164
  }
165
- .components-custom-select-control__label {
165
+
166
+ .components-custom-select-control__label,
167
+ .components-base-control__label {
166
168
  display: none;
167
169
  }
168
170
  }
@@ -5,7 +5,7 @@ import {
5
5
  isFreePlan,
6
6
  } from '@automattic/calypso-products';
7
7
  import styled from '@emotion/styled';
8
- import { useTranslate, formatCurrency } from 'i18n-calypso';
8
+ import { useTranslate, formatCurrency, fixMe } from 'i18n-calypso';
9
9
  import { usePlansGridContext } from '../../../grid-context';
10
10
  import usePlanBillingDescription from '../../../hooks/data-store/use-plan-billing-description';
11
11
  import type { GridPlan } from '../../../types';
@@ -78,14 +78,22 @@ const BillingTimeframe = ( { showRefundPeriod, planSlug }: Props ) => {
78
78
  }
79
79
 
80
80
  if ( isWpcomEnterpriseGridPlan( planSlug ) ) {
81
- const price = formatCurrency( 25000, 'USD' );
81
+ const price = formatCurrency( 25000, 'USD', { stripZeros: true } );
82
82
 
83
83
  return (
84
- <div className="plans-grid-next__billing-timeframe-vip-price">
85
- { translate( 'Starts at {{b}}%(price)s{{/b}} yearly', {
86
- args: { price },
87
- components: { b: <b /> },
88
- comment: 'Translators: the price is in US dollars for all users (US$25,000)',
84
+ <div>
85
+ { fixMe( {
86
+ text: 'Starts at {{b}}%(price)s{{/b}} annually',
87
+ newCopy: translate( 'Starts at {{b}}%(price)s{{/b}} annually', {
88
+ args: { price },
89
+ components: { b: <b /> },
90
+ comment: 'Translators: the price is in US dollars for all users (US$25,000)',
91
+ } ),
92
+ oldCopy: translate( 'Starts at {{b}}%(price)s{{/b}} yearly', {
93
+ args: { price },
94
+ components: { b: <b /> },
95
+ comment: 'Translators: the price is in US dollars for all users (US$25,000)',
96
+ } ),
89
97
  } ) }
90
98
  </div>
91
99
  );
@@ -76,9 +76,9 @@
76
76
  }
77
77
 
78
78
  .plans-grid-next-header-price__badge {
79
- background-color: var(--studio-green-0);
79
+ background-color: var(--studio-green-5);
80
80
  border-radius: 4px;
81
- color: var(--studio-green-40);
81
+ color: var(--studio-green-80);
82
82
  display: inline-block;
83
83
  font-size: 0.75rem;
84
84
  font-weight: 500;
@@ -1,7 +1,7 @@
1
1
  import { Global, css } from '@emotion/react';
2
2
  import styled from '@emotion/styled';
3
3
  import clsx from 'clsx';
4
- import { useRef, type ElementType, useState, useLayoutEffect, ReactNode } from 'react';
4
+ import { useRef, type ElementType, useState, useLayoutEffect, ReactNode, useEffect } from 'react';
5
5
 
6
6
  type Props = {
7
7
  /**
@@ -41,17 +41,19 @@ type Props = {
41
41
  const styles = ( {
42
42
  disabled,
43
43
  stickyOffset,
44
+ stickyPadding,
44
45
  zIndex,
45
46
  }: {
46
47
  disabled: boolean;
47
48
  stickyOffset: number;
49
+ stickyPadding: number;
48
50
  zIndex: number;
49
51
  } ) =>
50
52
  disabled
51
53
  ? ''
52
54
  : css`
53
55
  position: sticky;
54
- top: ${ stickyOffset + 'px' };
56
+ top: ${ stickyOffset - stickyPadding + 'px' };
55
57
  z-index: ${ zIndex };
56
58
  `;
57
59
 
@@ -59,6 +61,28 @@ const Container = styled.div`
59
61
  ${ styles }
60
62
  `;
61
63
 
64
+ /**
65
+ * Renders a sticky container.
66
+ *
67
+ * The following is an illustration on how the container's top property is calculated.
68
+ *
69
+ * +~~~~~~~~ stickyParent (where the container scrolls)
70
+ * stickyOffset ~~~~~~~~~~+ |
71
+ * (e.g. masterbar) | |
72
+ * v v
73
+ * +------#-----------------+
74
+ * | # |<~~~~~~~+~~~~~ stickyPadding
75
+ * | +----#--------------+ |
76
+ * | | # @ |<~+~~~~~ stickyParent's content area
77
+ * | | # @ | |
78
+ * | | # @ <~~~~~~~~+~~+~~~~~ the container's top
79
+ * | | # @ | | (= stickyOffset - stickyPadding)
80
+ * | | ============= | |
81
+ * | | ^ | |
82
+ * | | | | |
83
+ * | | +~~~~~~~~~~~~~+~~+~~~~~ the container
84
+ * | | | |
85
+ */
62
86
  export function StickyContainer( props: Props ) {
63
87
  const {
64
88
  stickyOffset = 0,
@@ -70,8 +94,53 @@ export function StickyContainer( props: Props ) {
70
94
  } = props;
71
95
 
72
96
  const stickyRef = useRef( null );
97
+ const [ stickyParent, setStickyParent ] = useState< HTMLElement | null >( null );
98
+ const [ stickyPadding, setStickyPadding ] = useState( 0 );
73
99
  const [ isStuck, setIsStuck ] = useState( false );
74
100
 
101
+ /**
102
+ * Calculate the first scrollable parent and its padding-top.
103
+ */
104
+ useEffect( () => {
105
+ if ( ! stickyRef.current || typeof getComputedStyle === 'undefined' ) {
106
+ return;
107
+ }
108
+
109
+ let scrollParent = stickyRef.current as HTMLElement | null;
110
+ while ( scrollParent ) {
111
+ const style = getComputedStyle( scrollParent );
112
+ const overflowY = style.overflowY;
113
+ if ( overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay' ) {
114
+ break;
115
+ }
116
+ scrollParent = scrollParent.parentElement;
117
+ }
118
+
119
+ const receiveScrollParent = ( el: HTMLElement ) => {
120
+ const styles = getComputedStyle( el );
121
+ const paddingTop = parseFloat( styles.paddingTop );
122
+ setStickyParent( scrollParent );
123
+ setStickyPadding( paddingTop );
124
+ };
125
+
126
+ if ( scrollParent ) {
127
+ scrollParent.style.position = 'relative';
128
+ receiveScrollParent( scrollParent );
129
+
130
+ if ( typeof ResizeObserver !== 'undefined' ) {
131
+ const observer = new ResizeObserver(
132
+ ( [ parent ]: Parameters< ResizeObserverCallback >[ 0 ] ) => {
133
+ receiveScrollParent( parent.target as HTMLElement );
134
+ }
135
+ );
136
+ observer.observe( scrollParent );
137
+ return () => {
138
+ observer.disconnect();
139
+ };
140
+ }
141
+ }
142
+ }, [] );
143
+
75
144
  /**
76
145
  * This effect sets the value of `isStuck` state when it detects that
77
146
  * the element is sticky.
@@ -102,6 +171,7 @@ export function StickyContainer( props: Props ) {
102
171
  }
103
172
  },
104
173
  {
174
+ root: stickyParent,
105
175
  rootMargin: `-${ stickyOffset + 1 }px 0px 0px 0px`,
106
176
  threshold: [ 0, 1 ],
107
177
  }
@@ -114,7 +184,7 @@ export function StickyContainer( props: Props ) {
114
184
  return () => {
115
185
  observer.disconnect();
116
186
  };
117
- }, [ disabled, stickyOffset ] );
187
+ }, [ disabled, stickyParent, stickyOffset, stickyPadding ] );
118
188
 
119
189
  const isStuckFinalState = ! disabled && isStuck;
120
190
  return (
@@ -135,6 +205,7 @@ export function StickyContainer( props: Props ) {
135
205
  as={ element }
136
206
  ref={ stickyRef }
137
207
  stickyOffset={ stickyOffset }
208
+ stickyPadding={ stickyPadding }
138
209
  disabled={ disabled }
139
210
  className={ clsx( { [ stickyClass ]: isStuckFinalState } ) }
140
211
  zIndex={ zIndex }
@@ -49,7 +49,7 @@ describe( 'BillingTimeframe', () => {
49
49
  jest.clearAllMocks();
50
50
  } );
51
51
 
52
- test( `should show savings with yearly when plan is monthly`, () => {
52
+ test( 'should show savings with yearly when plan is monthly', () => {
53
53
  const planMonthlyPricing = {
54
54
  currencyCode: 'USD',
55
55
  originalPrice: { full: 120, monthly: 10 },
@@ -309,7 +309,7 @@ describe( 'BillingTimeframe', () => {
309
309
  expect( getByText( /Refundable within 7 days. No questions asked./ ) ).toBeInTheDocument();
310
310
  } );
311
311
 
312
- test( `refund period can't be shown for free plan`, () => {
312
+ test( "refund period can't be shown for free plan", () => {
313
313
  const pricing = {
314
314
  currencyCode: 'USD',
315
315
  originalPrice: { full: 0, monthly: 0 },
@@ -10,6 +10,7 @@ export interface UseGridPlansParams {
10
10
  eligibleForFreeHostingTrial?: boolean;
11
11
  hasRedeemedDomainCredit?: boolean;
12
12
  hiddenPlans?: HiddenPlans;
13
+ hideCurrentPlan?: boolean;
13
14
  intent?: PlansIntent;
14
15
  isDisplayingPlansNeededForFeature?: boolean;
15
16
  isInSignup?: boolean;
@@ -10,6 +10,7 @@ const useGridPlansForFeaturesGrid = ( {
10
10
  eligibleForFreeHostingTrial,
11
11
  hasRedeemedDomainCredit,
12
12
  hiddenPlans,
13
+ hideCurrentPlan,
13
14
  intent,
14
15
  isDisplayingPlansNeededForFeature,
15
16
  isInSignup,
@@ -62,18 +63,21 @@ const useGridPlansForFeaturesGrid = ( {
62
63
  }
63
64
 
64
65
  return gridPlans.reduce( ( acc, gridPlan ) => {
65
- if ( gridPlan.isVisible ) {
66
- return [
67
- ...acc,
68
- {
69
- ...gridPlan,
70
- features: planFeaturesForFeaturesGrid[ gridPlan.planSlug ],
71
- },
72
- ];
66
+ if ( ! gridPlan.isVisible ) {
67
+ return acc;
73
68
  }
74
- return acc;
69
+ if ( hideCurrentPlan && gridPlan.current ) {
70
+ return acc;
71
+ }
72
+ return [
73
+ ...acc,
74
+ {
75
+ ...gridPlan,
76
+ features: planFeaturesForFeaturesGrid[ gridPlan.planSlug ],
77
+ },
78
+ ];
75
79
  }, [] as GridPlan[] );
76
- }, [ gridPlans, planFeaturesForFeaturesGrid ] );
80
+ }, [ gridPlans, planFeaturesForFeaturesGrid, hideCurrentPlan ] );
77
81
  };
78
82
 
79
83
  export default useGridPlansForFeaturesGrid;
@@ -73,7 +73,7 @@ export default function usePlanBillingDescription( {
73
73
  yearlyVariantMaybeDiscountedPrice &&
74
74
  yearlyVariantMaybeDiscountedPrice < originalPrice.monthly
75
75
  ) {
76
- return translate( `Save %(discountRate)s%% by paying annually`, {
76
+ return translate( 'Save %(discountRate)s%% by paying annually', {
77
77
  args: {
78
78
  discountRate: Math.floor(
79
79
  ( 100 * ( originalPrice.monthly - yearlyVariantMaybeDiscountedPrice ) ) /
@@ -1,5 +1,4 @@
1
1
  import { useLayoutEffect, useState } from '@wordpress/element';
2
- import ResizeObserver from 'resize-observer-polyfill';
3
2
 
4
3
  interface Props {
5
4
  containerRef: React.MutableRefObject< HTMLDivElement | null >;
@@ -1,46 +1,27 @@
1
- import { isFreePlan } from '@automattic/calypso-products';
2
- import { isPopularPlan } from '../hooks/data-store/is-popular-plan';
3
1
  import type { GridPlan } from '../types';
4
2
 
5
3
  export function sortPlans(
6
4
  gridPlans: GridPlan[],
7
- currentSitePlanProductSlug?: string | null,
8
- isMobile?: boolean
5
+ currentSitePlanProductSlug?: string | null
9
6
  ): GridPlan[] {
10
- let firstPlanIndex = -1;
11
-
12
- // Try to find the current paid plan in the plans list
13
- if ( currentSitePlanProductSlug && ! isFreePlan( currentSitePlanProductSlug ) ) {
14
- firstPlanIndex = gridPlans.findIndex( ( gridPlan ) => {
15
- return gridPlan.planSlug === currentSitePlanProductSlug;
16
- } );
7
+ // If we don't have plans to sort, return empty array
8
+ if ( ! gridPlans.length ) {
9
+ return [];
17
10
  }
18
-
19
- if ( firstPlanIndex < 0 ) {
20
- // Site is on a free plan or the current plan is not in the list. Try to find the popular plan.
21
- firstPlanIndex = gridPlans.findIndex( ( { planSlug } ) => {
22
- return isPopularPlan( planSlug );
11
+ // If we have a current site plan and we're on mobile, sort to prioritize it
12
+ if ( currentSitePlanProductSlug ) {
13
+ return [ ...gridPlans ].sort( ( planA, planB ) => {
14
+ // If planA is the current plan, it should come first (-1 moves it up)
15
+ if ( planA.planSlug === currentSitePlanProductSlug ) {
16
+ return -1;
17
+ }
18
+ // If planB is the current plan, it should come first (1 moves planA down)
19
+ if ( planB.planSlug === currentSitePlanProductSlug ) {
20
+ return 1;
21
+ }
22
+ // Otherwise, maintain original order
23
+ return 0;
23
24
  } );
24
- // If the popular plan exists, on mobile, it will be second plan for comparison.
25
- if ( firstPlanIndex > 0 && isMobile ) {
26
- firstPlanIndex = firstPlanIndex - 1;
27
- }
28
25
  }
29
-
30
- if ( firstPlanIndex < 0 ) {
31
- return gridPlans;
32
- }
33
-
34
- return [
35
- // The first plan
36
- gridPlans[ firstPlanIndex ],
37
- // Rest of the plans in default order
38
- ...gridPlans.slice( firstPlanIndex + 1 ),
39
- // Leftover plans (before the first plan) in descending order of value
40
- ...gridPlans.slice( 0, firstPlanIndex ).sort( ( planA, planB ) => {
41
- return (
42
- ( planB?.pricing.originalPrice.full || 0 ) - ( planA?.pricing.originalPrice.full || 0 )
43
- );
44
- } ),
45
- ].filter( ( gridPlan ) => Boolean( gridPlan ) );
26
+ return [ ...gridPlans ];
46
27
  }