@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.
- package/dist/cjs/__mocks__/wpcom-proxy-request.js +3 -4
- package/dist/cjs/__mocks__/wpcom-proxy-request.js.map +1 -1
- package/dist/cjs/_shared.scss +9 -4
- package/dist/cjs/components/comparison-grid/index.js +18 -29
- package/dist/cjs/components/comparison-grid/index.js.map +1 -1
- package/dist/cjs/components/comparison-grid/index.stories.js +1 -0
- package/dist/cjs/components/comparison-grid/index.stories.js.map +1 -1
- package/dist/cjs/components/comparison-grid/style.scss +0 -17
- package/dist/cjs/components/dropdown-option.js +1 -1
- package/dist/cjs/components/features-grid/index.js +21 -6
- package/dist/cjs/components/features-grid/index.js.map +1 -1
- package/dist/cjs/components/features-grid/index.stories.js +1 -0
- package/dist/cjs/components/features-grid/index.stories.js.map +1 -1
- package/dist/cjs/components/features-grid/style.scss +2 -19
- package/dist/cjs/components/item.js +1 -2
- package/dist/cjs/components/item.js.map +1 -1
- package/dist/cjs/components/plan-button/style.scss +8 -7
- package/dist/cjs/components/plan-type-selector/hooks/use-interval-options.js +1 -1
- package/dist/cjs/components/plan-type-selector/hooks/use-interval-options.js.map +1 -1
- package/dist/cjs/components/plan-type-selector/hooks/use-max-discount.js +1 -1
- package/dist/cjs/components/plan-type-selector/hooks/use-max-discount.js.map +1 -1
- package/dist/cjs/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.js +1 -1
- package/dist/cjs/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.js.map +1 -1
- package/dist/cjs/components/plan-type-selector/style.scss +3 -1
- package/dist/cjs/components/shared/billing-timeframe/index.js +13 -5
- package/dist/cjs/components/shared/billing-timeframe/index.js.map +1 -1
- package/dist/cjs/components/shared/header-price/style.scss +2 -2
- package/dist/cjs/components/shared/storage/hooks/use-default-storage-option.js +1 -1
- package/dist/cjs/components/shared/storage/hooks/use-default-storage-option.js.map +1 -1
- package/dist/cjs/components/shared/storage/hooks/use-purchased-storage-add-on.js +1 -1
- package/dist/cjs/components/shared/storage/hooks/use-purchased-storage-add-on.js.map +1 -1
- package/dist/cjs/components/shared/storage/index.js +3 -2
- package/dist/cjs/components/shared/storage/index.js.map +1 -1
- package/dist/cjs/components/sticky-container.js +66 -6
- package/dist/cjs/components/sticky-container.js.map +1 -1
- package/dist/cjs/hooks/data-store/is-popular-plan.js +1 -2
- package/dist/cjs/hooks/data-store/is-popular-plan.js.map +1 -1
- package/dist/cjs/hooks/data-store/use-grid-plans-for-features-grid.js +14 -11
- package/dist/cjs/hooks/data-store/use-grid-plans-for-features-grid.js.map +1 -1
- package/dist/cjs/hooks/data-store/use-plan-billing-description.js +2 -2
- package/dist/cjs/hooks/data-store/use-plan-billing-description.js.map +1 -1
- package/dist/cjs/hooks/use-grid-size.js +2 -4
- package/dist/cjs/hooks/use-grid-size.js.map +1 -1
- package/dist/cjs/hooks/use-is-large-currency.js +1 -1
- package/dist/cjs/hooks/use-is-large-currency.js.map +1 -1
- package/dist/cjs/hooks/use-manage-tooltip-toggle.js +1 -2
- package/dist/cjs/hooks/use-manage-tooltip-toggle.js.map +1 -1
- package/dist/cjs/hooks/use-plan-pricing-info-from-grid-plans.js +1 -2
- package/dist/cjs/hooks/use-plan-pricing-info-from-grid-plans.js.map +1 -1
- package/dist/cjs/lib/get-plan-pricing-info-from-grid-plans.js +1 -2
- package/dist/cjs/lib/get-plan-pricing-info-from-grid-plans.js.map +1 -1
- package/dist/cjs/lib/is-same-plan.js +1 -2
- package/dist/cjs/lib/is-same-plan.js.map +1 -1
- package/dist/cjs/lib/sort-plan-properties.js +19 -32
- package/dist/cjs/lib/sort-plan-properties.js.map +1 -1
- package/dist/cjs/lib/touch-detect/index.js +1 -2
- package/dist/cjs/lib/touch-detect/index.js.map +1 -1
- package/dist/esm/_shared.scss +9 -4
- package/dist/esm/components/comparison-grid/index.js +18 -29
- package/dist/esm/components/comparison-grid/index.js.map +1 -1
- package/dist/esm/components/comparison-grid/index.stories.js +1 -0
- package/dist/esm/components/comparison-grid/index.stories.js.map +1 -1
- package/dist/esm/components/comparison-grid/style.scss +0 -17
- package/dist/esm/components/dropdown-option.js +1 -1
- package/dist/esm/components/features-grid/index.js +21 -6
- package/dist/esm/components/features-grid/index.js.map +1 -1
- package/dist/esm/components/features-grid/index.stories.js +1 -0
- package/dist/esm/components/features-grid/index.stories.js.map +1 -1
- package/dist/esm/components/features-grid/style.scss +2 -19
- package/dist/esm/components/plan-button/style.scss +8 -7
- package/dist/esm/components/plan-type-selector/style.scss +3 -1
- package/dist/esm/components/shared/billing-timeframe/index.js +14 -6
- package/dist/esm/components/shared/billing-timeframe/index.js.map +1 -1
- package/dist/esm/components/shared/header-price/style.scss +2 -2
- package/dist/esm/components/sticky-container.js +66 -5
- package/dist/esm/components/sticky-container.js.map +1 -1
- package/dist/esm/hooks/data-store/use-grid-plans-for-features-grid.js +14 -11
- package/dist/esm/hooks/data-store/use-grid-plans-for-features-grid.js.map +1 -1
- package/dist/esm/hooks/data-store/use-plan-billing-description.js +1 -1
- package/dist/esm/hooks/use-grid-size.js +0 -1
- package/dist/esm/hooks/use-grid-size.js.map +1 -1
- package/dist/esm/lib/sort-plan-properties.js +18 -30
- package/dist/esm/lib/sort-plan-properties.js.map +1 -1
- package/dist/tsconfig-cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/components/comparison-grid/index.d.ts +1 -1
- package/dist/types/components/comparison-grid/index.d.ts.map +1 -1
- package/dist/types/components/comparison-grid/index.stories.d.ts +3 -1
- package/dist/types/components/comparison-grid/index.stories.d.ts.map +1 -1
- package/dist/types/components/dropdown-option.d.ts +0 -1
- package/dist/types/components/dropdown-option.d.ts.map +1 -1
- package/dist/types/components/features-grid/client-logo-list/client-list.d.ts +0 -1
- package/dist/types/components/features-grid/client-logo-list/client-list.d.ts.map +1 -1
- package/dist/types/components/features-grid/client-logo-list/index.d.ts.map +1 -1
- package/dist/types/components/features-grid/index.d.ts.map +1 -1
- package/dist/types/components/features-grid/index.stories.d.ts +5 -1
- package/dist/types/components/features-grid/index.stories.d.ts.map +1 -1
- package/dist/types/components/plan-button/index.d.ts +10 -10
- package/dist/types/components/plan-button/index.d.ts.map +1 -1
- package/dist/types/components/plan-div-td-container.d.ts +0 -1
- package/dist/types/components/plan-div-td-container.d.ts.map +1 -1
- package/dist/types/components/plan-logo.d.ts +0 -1
- package/dist/types/components/plan-logo.d.ts.map +1 -1
- package/dist/types/components/plan-type-selector/components/interval-type-dropdown.d.ts +0 -1
- package/dist/types/components/plan-type-selector/components/interval-type-dropdown.d.ts.map +1 -1
- package/dist/types/components/plan-type-selector/components/interval-type-selector.d.ts +0 -1
- package/dist/types/components/plan-type-selector/components/interval-type-selector.d.ts.map +1 -1
- package/dist/types/components/plan-type-selector/hooks/use-max-discounts-for-plan-terms.d.ts.map +1 -1
- package/dist/types/components/popular-badge.d.ts +0 -1
- package/dist/types/components/popular-badge.d.ts.map +1 -1
- package/dist/types/components/shared/billing-timeframe/index.d.ts.map +1 -1
- package/dist/types/components/shared/header-price/header-price-context.d.ts +0 -1
- package/dist/types/components/shared/header-price/header-price-context.d.ts.map +1 -1
- package/dist/types/components/shared/storage/hooks/use-storage-string.d.ts +0 -1
- package/dist/types/components/shared/storage/hooks/use-storage-string.d.ts.map +1 -1
- package/dist/types/components/sticky-container.d.ts +22 -0
- package/dist/types/components/sticky-container.d.ts.map +1 -1
- package/dist/types/grid-context.d.ts +0 -1
- package/dist/types/grid-context.d.ts.map +1 -1
- package/dist/types/hooks/data-store/types.d.ts +1 -0
- package/dist/types/hooks/data-store/types.d.ts.map +1 -1
- package/dist/types/hooks/data-store/use-grid-plans-for-features-grid.d.ts +1 -1
- package/dist/types/hooks/data-store/use-grid-plans-for-features-grid.d.ts.map +1 -1
- package/dist/types/hooks/data-store/use-grid-plans.d.ts +1 -1
- package/dist/types/hooks/data-store/use-grid-plans.d.ts.map +1 -1
- package/dist/types/hooks/data-store/use-highlight-labels.d.ts +0 -1
- package/dist/types/hooks/data-store/use-highlight-labels.d.ts.map +1 -1
- package/dist/types/hooks/data-store/use-plan-billing-description.d.ts +0 -1
- package/dist/types/hooks/data-store/use-plan-billing-description.d.ts.map +1 -1
- package/dist/types/hooks/use-grid-size.d.ts +0 -1
- package/dist/types/hooks/use-grid-size.d.ts.map +1 -1
- package/dist/types/lib/get-plan-features-object.d.ts.map +1 -1
- package/dist/types/lib/sort-plan-properties.d.ts +1 -1
- package/dist/types/lib/sort-plan-properties.d.ts.map +1 -1
- package/dist/types/types.d.ts +1 -1
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +14 -14
- package/src/_shared.scss +9 -4
- package/src/components/comparison-grid/index.stories.tsx +1 -0
- package/src/components/comparison-grid/index.tsx +19 -33
- package/src/components/comparison-grid/style.scss +0 -17
- package/src/components/dropdown-option.tsx +1 -1
- package/src/components/features-grid/index.stories.tsx +1 -0
- package/src/components/features-grid/index.tsx +21 -9
- package/src/components/features-grid/style.scss +2 -19
- package/src/components/plan-button/style.scss +8 -7
- package/src/components/plan-type-selector/style.scss +3 -1
- package/src/components/shared/billing-timeframe/index.tsx +15 -7
- package/src/components/shared/header-price/style.scss +2 -2
- package/src/components/sticky-container.tsx +74 -3
- package/src/components/test/billing-timeframe.tsx +2 -2
- package/src/hooks/data-store/types.ts +1 -0
- package/src/hooks/data-store/use-grid-plans-for-features-grid.ts +14 -10
- package/src/hooks/data-store/use-plan-billing-description.tsx +1 -1
- package/src/hooks/use-grid-size.ts +0 -1
- package/src/lib/sort-plan-properties.ts +18 -37
- package/src/lib/test/sort-plan-properties.ts +50 -32
- 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
|
|
940
|
-
}, [ gridPlans, currentSitePlanSlug
|
|
939
|
+
return sortPlans( gridPlans, currentSitePlanSlug );
|
|
940
|
+
}, [ gridPlans, currentSitePlanSlug ] );
|
|
941
941
|
|
|
942
942
|
useEffect( () => {
|
|
943
|
-
setVisiblePlans( (
|
|
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
|
-
|
|
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:
|
|
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 {
|
|
@@ -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
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
112
|
+
background-color: var(--studio-woocommerce-purple-50);
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
|
|
@@ -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
|
|
85
|
-
{
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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-
|
|
79
|
+
background-color: var(--studio-green-5);
|
|
80
80
|
border-radius: 4px;
|
|
81
|
-
color: var(--studio-green-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
76
|
+
return translate( 'Save %(discountRate)s%% by paying annually', {
|
|
77
77
|
args: {
|
|
78
78
|
discountRate: Math.floor(
|
|
79
79
|
( 100 * ( originalPrice.monthly - yearlyVariantMaybeDiscountedPrice ) ) /
|
|
@@ -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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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 (
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
}
|