@automattic/jetpack-components 1.1.18 → 1.2.0
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/CHANGELOG.md +10 -0
- package/build/components/boost-score-graph/tooltip.js +2 -2
- package/build/components/pricing-table/index.js +6 -6
- package/build/components/pricing-table/types.d.ts +12 -0
- package/build/components/product-price/index.js +6 -1
- package/build/components/product-price/price.js +4 -3
- package/build/components/product-price/types.d.ts +8 -0
- package/components/boost-score-graph/tooltip.tsx +6 -6
- package/components/pricing-table/gradient.svg +17 -0
- package/components/pricing-table/index.tsx +17 -5
- package/components/pricing-table/styles.module.scss +74 -22
- package/components/pricing-table/types.ts +15 -0
- package/components/product-price/index.tsx +35 -0
- package/components/product-price/price.tsx +21 -5
- package/components/product-price/style.module.scss +43 -0
- package/components/product-price/types.ts +10 -0
- package/package.json +13 -13
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
### This is a list detailing changes for the Jetpack RNA Components package releases.
|
|
4
4
|
|
|
5
|
+
## [1.2.0] - 2025-08-14
|
|
6
|
+
### Added
|
|
7
|
+
- Add new Interstitial component. [#44665]
|
|
8
|
+
|
|
9
|
+
## [1.1.19] - 2025-08-13
|
|
10
|
+
### Changed
|
|
11
|
+
- Update package dependencies. [#44701]
|
|
12
|
+
|
|
5
13
|
## [1.1.18] - 2025-08-11
|
|
6
14
|
### Changed
|
|
7
15
|
- Update package dependencies. [#44677]
|
|
@@ -1500,6 +1508,8 @@
|
|
|
1500
1508
|
### Changed
|
|
1501
1509
|
- Update node version requirement to 14.16.1
|
|
1502
1510
|
|
|
1511
|
+
[1.2.0]: https://github.com/Automattic/jetpack-components/compare/1.1.19...1.2.0
|
|
1512
|
+
[1.1.19]: https://github.com/Automattic/jetpack-components/compare/1.1.18...1.1.19
|
|
1503
1513
|
[1.1.18]: https://github.com/Automattic/jetpack-components/compare/1.1.17...1.1.18
|
|
1504
1514
|
[1.1.17]: https://github.com/Automattic/jetpack-components/compare/1.1.16...1.1.17
|
|
1505
1515
|
[1.1.16]: https://github.com/Automattic/jetpack-components/compare/1.1.15...1.1.16
|
|
@@ -16,7 +16,7 @@ export const Tooltip = ({ period }) => {
|
|
|
16
16
|
}
|
|
17
17
|
return (_jsxs("div", { className: "jb-score-tooltip", children: [_jsx("div", { className: "jb-score-tooltip__date", children: date }), _jsxs("div", { className: "jb-score-tooltip__row", children: [_jsx("div", { className: "jb-score-tooltip__column", children: __('Overall score', 'jetpack-components') }), _jsx("div", { className: "jb-score-tooltip__column", children: scoreLetter })] }), _jsx("hr", {}), _jsxs("div", { className: "jb-score-tooltip__row", children: [_jsx("div", { className: "jb-score-tooltip__column", children: __('Desktop score', 'jetpack-components') }), _jsx("div", { className: "jb-score-tooltip__column", children:
|
|
18
18
|
/* translators: %d is the score */
|
|
19
|
-
sprintf(__('%d / 100', 'jetpack-components'), desktop_overall_score) })] }), typeof desktop_lcp === 'number' && (_jsxs("div", { className: "jb-score-tooltip__row jb-score-tooltip__row--secondary", children: [_jsx("div", { className: "jb-score-tooltip__column", children: __('Largest Contentful Paint', 'jetpack-components') }), _jsx("div", { className: "jb-score-tooltip__column", children: sprintf('
|
|
19
|
+
sprintf(__('%d / 100', 'jetpack-components'), desktop_overall_score) })] }), typeof desktop_lcp === 'number' && (_jsxs("div", { className: "jb-score-tooltip__row jb-score-tooltip__row--secondary", children: [_jsx("div", { className: "jb-score-tooltip__column", children: __('Largest Contentful Paint', 'jetpack-components') }), _jsx("div", { className: "jb-score-tooltip__column", children: sprintf('%.2fs', desktop_lcp) })] })), typeof desktop_tbt === 'number' && (_jsxs("div", { className: "jb-score-tooltip__row jb-score-tooltip__row--secondary", children: [_jsx("div", { className: "jb-score-tooltip__column", children: __('Total Blocking Time', 'jetpack-components') }), _jsx("div", { className: "jb-score-tooltip__column", children: sprintf('%.2fs', desktop_tbt) })] })), typeof desktop_cls === 'number' && (_jsxs("div", { className: "jb-score-tooltip__row jb-score-tooltip__row--secondary", children: [_jsx("div", { className: "jb-score-tooltip__column", children: __('Cumulative Layout Shift', 'jetpack-components') }), _jsx("div", { className: "jb-score-tooltip__column", children: sprintf('%.2f', desktop_cls) })] })), _jsx("hr", {}), _jsxs("div", { className: "jb-score-tooltip__row", children: [_jsx("div", { className: "jb-score-tooltip__column", children: __('Mobile score', 'jetpack-components') }), _jsx("div", { className: "jb-score-tooltip__column", children:
|
|
20
20
|
/* translators: %d is the score */
|
|
21
|
-
sprintf(__('%d / 100', 'jetpack-components'), mobile_overall_score) })] }), typeof mobile_lcp === 'number' && (_jsxs("div", { className: "jb-score-tooltip__row jb-score-tooltip__row--secondary", children: [_jsx("div", { className: "jb-score-tooltip__column", children: __('Largest Contentful Paint', 'jetpack-components') }), _jsx("div", { className: "jb-score-tooltip__column", children: sprintf('
|
|
21
|
+
sprintf(__('%d / 100', 'jetpack-components'), mobile_overall_score) })] }), typeof mobile_lcp === 'number' && (_jsxs("div", { className: "jb-score-tooltip__row jb-score-tooltip__row--secondary", children: [_jsx("div", { className: "jb-score-tooltip__column", children: __('Largest Contentful Paint', 'jetpack-components') }), _jsx("div", { className: "jb-score-tooltip__column", children: sprintf('%.2fs', mobile_lcp) })] })), typeof mobile_tbt === 'number' && (_jsxs("div", { className: "jb-score-tooltip__row jb-score-tooltip__row--secondary", children: [_jsx("div", { className: "jb-score-tooltip__column", children: __('Total Blocking Time', 'jetpack-components') }), _jsx("div", { className: "jb-score-tooltip__column", children: sprintf('%.2fs', mobile_tbt) })] })), typeof mobile_cls === 'number' && (_jsxs("div", { className: "jb-score-tooltip__row jb-score-tooltip__row--secondary", children: [_jsx("div", { className: "jb-score-tooltip__column", children: __('Cumulative Layout Shift', 'jetpack-components') }), _jsx("div", { className: "jb-score-tooltip__column", children: sprintf('%.2f', mobile_cls) })] })), _jsx("div", { className: "jb-score-tooltip__pointer" })] }));
|
|
22
22
|
};
|
|
@@ -41,10 +41,10 @@ export const PricingTableItem = ({ isIncluded = false, isComingSoon = false, ind
|
|
|
41
41
|
const defaultLabel = isLg ? labels.lg : labels.default;
|
|
42
42
|
return (_jsxs("div", { className: clsx(styles.item, styles.value), children: [_jsx(Icon, { className: clsx(styles.icon, showTick ? styles['icon-check'] : styles['icon-cross']), size: 32, icon: showTick ? check : closeSmall }), _jsx(Text, { variant: "body-small", children: label || defaultLabel }), showTooltip && (_jsx(IconTooltip, { title: tooltipTitle ? tooltipTitle : defaultTooltipTitle, iconClassName: styles['popover-icon'], className: clsx(styles.popover, tooltipClassName), placement: 'bottom-end', iconSize: 14, offset: 4, wide: Boolean(tooltipTitle && tooltipInfo), children: _jsx(Text, { variant: "body-small", component: "div", children: tooltipInfo || defaultTooltipInfo }) }))] }));
|
|
43
43
|
};
|
|
44
|
-
export const PricingTableHeader = ({ children }) => (_jsx("div", { className: styles.header, children: children }));
|
|
45
|
-
export const PricingTableColumn = ({ primary = false, children, }) => {
|
|
44
|
+
export const PricingTableHeader = ({ title, children }) => (_jsxs("div", { className: styles.headerContainer, children: [title && (_jsx(Text, { variant: "headline-small", className: styles.title, children: title })), _jsx("div", { className: styles.header, children: children })] }));
|
|
45
|
+
export const PricingTableColumn = ({ primary = false, children, className, }) => {
|
|
46
46
|
let index = 0;
|
|
47
|
-
return (_jsx("div", { className: clsx(styles.card, { [styles['is-primary']]: primary }), children: Children.map(children, child => {
|
|
47
|
+
return (_jsx("div", { className: clsx(styles.card, { [styles['is-primary']]: primary }, className), children: Children.map(children, child => {
|
|
48
48
|
const item = child;
|
|
49
49
|
if (item.type === PricingTableItem) {
|
|
50
50
|
index++;
|
|
@@ -53,13 +53,13 @@ export const PricingTableColumn = ({ primary = false, children, }) => {
|
|
|
53
53
|
return item;
|
|
54
54
|
}) }));
|
|
55
55
|
};
|
|
56
|
-
const PricingTable = ({ title, items, children, showIntroOfferDisclaimer = false, }) => {
|
|
56
|
+
const PricingTable = ({ title, headerLogo, items, children, showIntroOfferDisclaimer = false, }) => {
|
|
57
57
|
const [isLg] = useBreakpointMatch('lg');
|
|
58
58
|
return (_jsxs(PricingTableContext.Provider, { value: items, children: [_jsx("div", { className: clsx(styles.container, { [styles['is-viewport-large']]: isLg }), style: {
|
|
59
59
|
'--rows': items.length + 1,
|
|
60
60
|
'--columns': Children.toArray(children).length + 1,
|
|
61
|
-
}, children: _jsxs("div", { className: styles.table, children: [_jsx(Text, { variant: "headline-small", children: title }), isLg &&
|
|
62
|
-
items.map((item, i) => (_jsxs("div", { className: clsx(styles.item, {
|
|
61
|
+
}, children: _jsxs("div", { className: styles.table, children: [_jsxs("div", { children: [headerLogo && _jsx("div", { className: styles['header-logo'], children: headerLogo }), _jsx(Text, { variant: "headline-small", children: title })] }), isLg &&
|
|
62
|
+
items.map((item, i) => (_jsxs("div", { className: clsx(styles.item, styles.feature, {
|
|
63
63
|
[styles['last-feature']]: i === items.length - 1,
|
|
64
64
|
}), children: [_jsx(Text, { variant: "body-small", children: _jsx("strong", { children: item.name }) }), item.tooltipInfo && (_jsx(IconTooltip, { title: item.tooltipTitle, iconClassName: styles['popover-icon'], className: styles.popover, placement: item.tooltipPlacement ? item.tooltipPlacement : 'bottom-end', iconSize: 14, offset: 4, wide: Boolean(item.tooltipTitle && item.tooltipInfo), children: _jsx(Text, { variant: "body-small", children: item.tooltipInfo }) }))] }, i))), children] }) }), _jsx("div", { className: styles['tos-container'], children: _jsxs("div", { className: styles.tos, children: [showIntroOfferDisclaimer && (_jsx(Text, { variant: "body-small", children: __('Reduced pricing is a limited offer for the first year and renews at regular price.', 'jetpack-components') })), _jsx(TermsOfService, { multipleButtons: true })] }) })] }));
|
|
65
65
|
};
|
|
@@ -5,6 +5,10 @@ export type PricingTableProps = {
|
|
|
5
5
|
* Title of the pricing table.
|
|
6
6
|
*/
|
|
7
7
|
title: string;
|
|
8
|
+
/**
|
|
9
|
+
* Optional header logo to display above the title.
|
|
10
|
+
*/
|
|
11
|
+
headerLogo?: ReactNode;
|
|
8
12
|
/**
|
|
9
13
|
* Array of items to display in the pricing table.
|
|
10
14
|
*/
|
|
@@ -32,8 +36,16 @@ export type PricingTableColumnProps = {
|
|
|
32
36
|
* Items to show in a column.
|
|
33
37
|
*/
|
|
34
38
|
children: ReactElement[];
|
|
39
|
+
/**
|
|
40
|
+
* Additional CSS class name to apply to the column.
|
|
41
|
+
*/
|
|
42
|
+
className?: string;
|
|
35
43
|
};
|
|
36
44
|
export type PricingTableHeaderProps = {
|
|
45
|
+
/**
|
|
46
|
+
* Title of the header.
|
|
47
|
+
*/
|
|
48
|
+
title?: string;
|
|
37
49
|
/**
|
|
38
50
|
* Items to show in a header.
|
|
39
51
|
*/
|
|
@@ -16,7 +16,7 @@ import styles from './style.module.scss';
|
|
|
16
16
|
* @param {ProductPriceProps} props - Component props.
|
|
17
17
|
* @return {ReactNode} Price react component.
|
|
18
18
|
*/
|
|
19
|
-
const ProductPrice = ({ price, offPrice, currency = '', showNotOffPrice = true, hideDiscountLabel = true, promoLabel = '', legend = __('/month, paid yearly', 'jetpack-components'), isNotConvenientPrice = false, hidePriceFraction = false, children, }) => {
|
|
19
|
+
const ProductPrice = ({ price, offPrice, currency = '', showNotOffPrice = true, hideDiscountLabel = true, promoLabel = '', legend = __('/month, paid yearly', 'jetpack-components'), isNotConvenientPrice = false, hidePriceFraction = false, children, variant = 'default', }) => {
|
|
20
20
|
if ((price == null && offPrice == null) || !currency) {
|
|
21
21
|
return null;
|
|
22
22
|
}
|
|
@@ -26,6 +26,11 @@ const ProductPrice = ({ price, offPrice, currency = '', showNotOffPrice = true,
|
|
|
26
26
|
: 0;
|
|
27
27
|
const showDiscountLabel = !hideDiscountLabel && discount && discount > 0;
|
|
28
28
|
const discountElt = showDiscountLabel ? discount + __('% off', 'jetpack-components') : null;
|
|
29
|
+
if (variant === 'simple') {
|
|
30
|
+
return (_jsxs("div", { className: styles.simple, children: [_jsxs("div", { className: styles.currentPrice, children: [_jsx(Price, { value: offPrice ?? price, currency: currency, isOff: !isNotConvenientPrice, hidePriceFraction: hidePriceFraction, inline: true }), _jsx("div", { children: legend })] }), showNotOffPrice && (_jsxs("div", { className: styles.originalPrice, children: [_jsx(Price, { value: price, currency: currency, isOff: false, hidePriceFraction: hidePriceFraction, inline: true }), _jsx("div", { children: discount &&
|
|
31
|
+
discount > 0 &&
|
|
32
|
+
discount + __('% off the first year', 'jetpack-components') })] }))] }));
|
|
33
|
+
}
|
|
29
34
|
return (_jsxs(_Fragment, { children: [_jsx("div", { className: styles.container, children: _jsxs("div", { className: clsx(styles['price-container'], 'product-price_container'), children: [_jsx(Price, { value: offPrice ?? price, currency: currency, isOff: !isNotConvenientPrice, hidePriceFraction: hidePriceFraction }), showNotOffPrice && (_jsx(Price, { value: price, currency: currency, isOff: false, hidePriceFraction: hidePriceFraction })), discountElt && (_jsx(Text, { className: clsx(styles['promo-label'], 'product-price_promo_label'), children: discountElt }))] }) }), _jsxs("div", { className: styles.footer, children: [children ? (children) : (_jsx(Text, { className: clsx(styles.legend, 'product-price_legend'), children: legend })), promoLabel && (_jsx(Text, { className: clsx(styles['promo-label'], 'product-price_promo_label'), children: promoLabel }))] })] }));
|
|
30
35
|
};
|
|
31
36
|
export default ProductPrice;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { getCurrencyObject } from '@automattic/number-formatters';
|
|
3
3
|
import clsx from 'clsx';
|
|
4
4
|
import Text from "../text/index.js";
|
|
@@ -9,11 +9,12 @@ import styles from './style.module.scss';
|
|
|
9
9
|
* @param {PriceProps} props - Component props.
|
|
10
10
|
* @return {ReactNode} -Price react component.
|
|
11
11
|
*/
|
|
12
|
-
export const Price = ({ value, currency, isOff, hidePriceFraction }) => {
|
|
12
|
+
export const Price = ({ value, currency, isOff, hidePriceFraction, inline = false, }) => {
|
|
13
13
|
const classNames = clsx(styles.price, 'product-price_price', {
|
|
14
14
|
[styles['is-not-off-price']]: !isOff,
|
|
15
|
+
[styles['price-inline']]: inline,
|
|
15
16
|
});
|
|
16
17
|
const { symbol, integer, fraction } = getCurrencyObject(value, currency);
|
|
17
18
|
const showPriceFraction = !hidePriceFraction || !fraction.endsWith('00');
|
|
18
|
-
return (_jsxs(Text, { className: classNames, variant: "headline-medium", component: "p", children: [_jsx(Text, { className: styles.symbol, component: "sup", variant: "title-medium", children: symbol }), integer, showPriceFraction && (_jsx(Text, { component: "sup", variant: "body-small", "data-testid": "PriceFraction", children: _jsx("strong", { children: fraction }) }))] }));
|
|
19
|
+
return (_jsxs(Text, { className: classNames, variant: "headline-medium", component: "p", children: [inline ? (_jsxs(_Fragment, { children: [symbol, integer] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { className: styles.symbol, component: "sup", variant: "title-medium", children: symbol }), integer] })), showPriceFraction && (_jsx(Text, { component: "sup", variant: "body-small", "data-testid": "PriceFraction", children: _jsx("strong", { children: fraction }) }))] }));
|
|
19
20
|
};
|
|
@@ -40,6 +40,10 @@ export type ProductPriceProps = {
|
|
|
40
40
|
* Alternative legend with HTML syntax
|
|
41
41
|
*/
|
|
42
42
|
children?: ReactNode;
|
|
43
|
+
/**
|
|
44
|
+
* Component variant
|
|
45
|
+
*/
|
|
46
|
+
variant?: 'default' | 'simple';
|
|
43
47
|
};
|
|
44
48
|
export type PriceProps = {
|
|
45
49
|
/**
|
|
@@ -58,4 +62,8 @@ export type PriceProps = {
|
|
|
58
62
|
* Hides the price fraction if fraction is zero.
|
|
59
63
|
*/
|
|
60
64
|
hidePriceFraction?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Inline layout - symbol same size and positioned inline with the number.
|
|
67
|
+
*/
|
|
68
|
+
inline?: boolean;
|
|
61
69
|
};
|
|
@@ -53,7 +53,7 @@ export const Tooltip: FunctionComponent = ( { period }: { period: Period } ) =>
|
|
|
53
53
|
<div className="jb-score-tooltip__column">
|
|
54
54
|
{ __( 'Largest Contentful Paint', 'jetpack-components' ) }
|
|
55
55
|
</div>
|
|
56
|
-
<div className="jb-score-tooltip__column">{ sprintf( '
|
|
56
|
+
<div className="jb-score-tooltip__column">{ sprintf( '%.2fs', desktop_lcp ) }</div>
|
|
57
57
|
</div>
|
|
58
58
|
) }
|
|
59
59
|
{ typeof desktop_tbt === 'number' && (
|
|
@@ -61,7 +61,7 @@ export const Tooltip: FunctionComponent = ( { period }: { period: Period } ) =>
|
|
|
61
61
|
<div className="jb-score-tooltip__column">
|
|
62
62
|
{ __( 'Total Blocking Time', 'jetpack-components' ) }
|
|
63
63
|
</div>
|
|
64
|
-
<div className="jb-score-tooltip__column">{ sprintf( '
|
|
64
|
+
<div className="jb-score-tooltip__column">{ sprintf( '%.2fs', desktop_tbt ) }</div>
|
|
65
65
|
</div>
|
|
66
66
|
) }
|
|
67
67
|
{ typeof desktop_cls === 'number' && (
|
|
@@ -69,7 +69,7 @@ export const Tooltip: FunctionComponent = ( { period }: { period: Period } ) =>
|
|
|
69
69
|
<div className="jb-score-tooltip__column">
|
|
70
70
|
{ __( 'Cumulative Layout Shift', 'jetpack-components' ) }
|
|
71
71
|
</div>
|
|
72
|
-
<div className="jb-score-tooltip__column">{ sprintf( '
|
|
72
|
+
<div className="jb-score-tooltip__column">{ sprintf( '%.2f', desktop_cls ) }</div>
|
|
73
73
|
</div>
|
|
74
74
|
) }
|
|
75
75
|
<hr />
|
|
@@ -89,7 +89,7 @@ export const Tooltip: FunctionComponent = ( { period }: { period: Period } ) =>
|
|
|
89
89
|
<div className="jb-score-tooltip__column">
|
|
90
90
|
{ __( 'Largest Contentful Paint', 'jetpack-components' ) }
|
|
91
91
|
</div>
|
|
92
|
-
<div className="jb-score-tooltip__column">{ sprintf( '
|
|
92
|
+
<div className="jb-score-tooltip__column">{ sprintf( '%.2fs', mobile_lcp ) }</div>
|
|
93
93
|
</div>
|
|
94
94
|
) }
|
|
95
95
|
{ typeof mobile_tbt === 'number' && (
|
|
@@ -97,7 +97,7 @@ export const Tooltip: FunctionComponent = ( { period }: { period: Period } ) =>
|
|
|
97
97
|
<div className="jb-score-tooltip__column">
|
|
98
98
|
{ __( 'Total Blocking Time', 'jetpack-components' ) }
|
|
99
99
|
</div>
|
|
100
|
-
<div className="jb-score-tooltip__column">{ sprintf( '
|
|
100
|
+
<div className="jb-score-tooltip__column">{ sprintf( '%.2fs', mobile_tbt ) }</div>
|
|
101
101
|
</div>
|
|
102
102
|
) }
|
|
103
103
|
{ typeof mobile_cls === 'number' && (
|
|
@@ -105,7 +105,7 @@ export const Tooltip: FunctionComponent = ( { period }: { period: Period } ) =>
|
|
|
105
105
|
<div className="jb-score-tooltip__column">
|
|
106
106
|
{ __( 'Cumulative Layout Shift', 'jetpack-components' ) }
|
|
107
107
|
</div>
|
|
108
|
-
<div className="jb-score-tooltip__column">{ sprintf( '
|
|
108
|
+
<div className="jb-score-tooltip__column">{ sprintf( '%.2f', mobile_cls ) }</div>
|
|
109
109
|
</div>
|
|
110
110
|
) }
|
|
111
111
|
<div className="jb-score-tooltip__pointer"></div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<svg width="380" height="183" viewBox="0 0 380 183" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g opacity="0.75" filter="url(#filter0_f_5953_11924)">
|
|
3
|
+
<path d="M360 -60C360 7.93102 304.931 63 237 63C169.069 63 114 7.93102 114 -60C114 -127.931 169.069 -183 237 -183C304.931 -183 360 -127.931 360 -60Z" fill="url(#paint0_linear_5953_11924)"/>
|
|
4
|
+
</g>
|
|
5
|
+
<defs>
|
|
6
|
+
<filter id="filter0_f_5953_11924" x="-6" y="-303" width="486" height="486" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
7
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
8
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
9
|
+
<feGaussianBlur stdDeviation="60" result="effect1_foregroundBlur_5953_11924"/>
|
|
10
|
+
</filter>
|
|
11
|
+
<linearGradient id="paint0_linear_5953_11924" x1="360" y1="-60" x2="114" y2="-60" gradientUnits="userSpaceOnUse">
|
|
12
|
+
<stop stop-color="#48FF50"/>
|
|
13
|
+
<stop offset="0.471846" stop-color="#108642"/>
|
|
14
|
+
<stop offset="1" stop-color="#2E38FA"/>
|
|
15
|
+
</linearGradient>
|
|
16
|
+
</defs>
|
|
17
|
+
</svg>
|
|
@@ -103,18 +103,26 @@ export const PricingTableItem: FC< PricingTableItemProps > = ( {
|
|
|
103
103
|
);
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
-
export const PricingTableHeader: FC< PricingTableHeaderProps > = ( { children } ) => (
|
|
107
|
-
<div className={ styles.
|
|
106
|
+
export const PricingTableHeader: FC< PricingTableHeaderProps > = ( { title, children } ) => (
|
|
107
|
+
<div className={ styles.headerContainer }>
|
|
108
|
+
{ title && (
|
|
109
|
+
<Text variant="headline-small" className={ styles.title }>
|
|
110
|
+
{ title }
|
|
111
|
+
</Text>
|
|
112
|
+
) }
|
|
113
|
+
<div className={ styles.header }>{ children }</div>
|
|
114
|
+
</div>
|
|
108
115
|
);
|
|
109
116
|
|
|
110
117
|
export const PricingTableColumn: FC< PricingTableColumnProps > = ( {
|
|
111
118
|
primary = false,
|
|
112
119
|
children,
|
|
120
|
+
className,
|
|
113
121
|
} ) => {
|
|
114
122
|
let index = 0;
|
|
115
123
|
|
|
116
124
|
return (
|
|
117
|
-
<div className={ clsx( styles.card, { [ styles[ 'is-primary' ] ]: primary } ) }>
|
|
125
|
+
<div className={ clsx( styles.card, { [ styles[ 'is-primary' ] ]: primary }, className ) }>
|
|
118
126
|
{ Children.map( children, child => {
|
|
119
127
|
const item = child as ReactElement<
|
|
120
128
|
PropsWithChildren< PricingTableHeaderProps | PricingTableItemProps >
|
|
@@ -133,6 +141,7 @@ export const PricingTableColumn: FC< PricingTableColumnProps > = ( {
|
|
|
133
141
|
|
|
134
142
|
const PricingTable: FC< PricingTableProps > = ( {
|
|
135
143
|
title,
|
|
144
|
+
headerLogo,
|
|
136
145
|
items,
|
|
137
146
|
children,
|
|
138
147
|
showIntroOfferDisclaimer = false,
|
|
@@ -151,11 +160,14 @@ const PricingTable: FC< PricingTableProps > = ( {
|
|
|
151
160
|
}
|
|
152
161
|
>
|
|
153
162
|
<div className={ styles.table }>
|
|
154
|
-
<
|
|
163
|
+
<div>
|
|
164
|
+
{ headerLogo && <div className={ styles[ 'header-logo' ] }>{ headerLogo }</div> }
|
|
165
|
+
<Text variant="headline-small">{ title }</Text>
|
|
166
|
+
</div>
|
|
155
167
|
{ isLg &&
|
|
156
168
|
items.map( ( item, i ) => (
|
|
157
169
|
<div
|
|
158
|
-
className={ clsx( styles.item, {
|
|
170
|
+
className={ clsx( styles.item, styles.feature, {
|
|
159
171
|
[ styles[ 'last-feature' ] ]: i === items.length - 1,
|
|
160
172
|
} ) }
|
|
161
173
|
key={ i }
|
|
@@ -17,6 +17,15 @@
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
.header-logo {
|
|
21
|
+
|
|
22
|
+
.is-viewport-large & {
|
|
23
|
+
margin-top: 32px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
margin-bottom: 24px;
|
|
27
|
+
}
|
|
28
|
+
|
|
20
29
|
.card {
|
|
21
30
|
margin-top: var(--padding);
|
|
22
31
|
|
|
@@ -24,6 +33,27 @@
|
|
|
24
33
|
display: contents;
|
|
25
34
|
}
|
|
26
35
|
|
|
36
|
+
// Border styles for all columns (default gray borders)
|
|
37
|
+
> :first-child {
|
|
38
|
+
border-style: solid;
|
|
39
|
+
border-color: var(--jp-gray-10);
|
|
40
|
+
border-width: 1.5px 1.5px 0;
|
|
41
|
+
border-start-start-radius: 8px;
|
|
42
|
+
border-start-end-radius: 8px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
> :last-child {
|
|
46
|
+
border-style: solid;
|
|
47
|
+
border-color: var(--jp-gray-10);
|
|
48
|
+
border-width: 0 1.5px 1.5px;
|
|
49
|
+
border-end-start-radius: 8px;
|
|
50
|
+
border-end-end-radius: 8px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
> :not(:first-child):not(:last-child) {
|
|
54
|
+
border-inline: 1.5px solid var(--jp-gray-10);
|
|
55
|
+
}
|
|
56
|
+
|
|
27
57
|
&.is-primary {
|
|
28
58
|
|
|
29
59
|
> * {
|
|
@@ -41,26 +71,50 @@
|
|
|
41
71
|
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.05);
|
|
42
72
|
}
|
|
43
73
|
}
|
|
44
|
-
}
|
|
45
74
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
75
|
+
// Primary column with green border and gradient background
|
|
76
|
+
> :first-child {
|
|
77
|
+
border-style: solid;
|
|
78
|
+
border-color: #008710;
|
|
79
|
+
border-width: 1.5px 1.5px 0;
|
|
80
|
+
border-start-start-radius: 8px;
|
|
81
|
+
border-start-end-radius: 8px;
|
|
82
|
+
|
|
83
|
+
background-image: url(./gradient.svg);
|
|
84
|
+
background-repeat: no-repeat;
|
|
85
|
+
background-size: 450px;
|
|
86
|
+
background-position-x: 170px;
|
|
87
|
+
}
|
|
51
88
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
89
|
+
> :last-child {
|
|
90
|
+
border-style: solid;
|
|
91
|
+
border-color: #008710;
|
|
92
|
+
border-width: 0 1.5px 1.5px;
|
|
93
|
+
border-end-start-radius: 8px;
|
|
94
|
+
border-end-end-radius: 8px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
> :not(:first-child):not(:last-child) {
|
|
98
|
+
border-inline: 1.5px solid #008710;
|
|
99
|
+
}
|
|
56
100
|
}
|
|
57
101
|
}
|
|
58
102
|
|
|
59
|
-
.
|
|
103
|
+
.headerContainer {
|
|
60
104
|
padding: var(--padding);
|
|
61
105
|
display: flex;
|
|
62
106
|
flex-direction: column;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.header {
|
|
110
|
+
display: flex;
|
|
111
|
+
flex-direction: column;
|
|
63
112
|
justify-content: space-between;
|
|
113
|
+
flex-basis: 100%;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.title {
|
|
117
|
+
margin-bottom: 24px;
|
|
64
118
|
}
|
|
65
119
|
|
|
66
120
|
.item {
|
|
@@ -76,22 +130,20 @@
|
|
|
76
130
|
content: "";
|
|
77
131
|
position: absolute;
|
|
78
132
|
top: 0;
|
|
79
|
-
|
|
80
|
-
|
|
133
|
+
inset-inline-start: var(--padding);
|
|
134
|
+
inset-inline-end: var(--padding);
|
|
81
135
|
height: 1px;
|
|
82
136
|
|
|
83
|
-
.is-viewport-large & {
|
|
84
|
-
width: calc(100% + var(--gap));
|
|
85
|
-
left: 0;
|
|
86
|
-
right: unset;
|
|
87
|
-
}
|
|
88
137
|
z-index: 5;
|
|
89
138
|
background-color: var(--jp-gray);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
&.feature {
|
|
142
|
+
|
|
143
|
+
&::before {
|
|
144
|
+
inset-inline-start: 0;
|
|
145
|
+
inset-inline-end: calc(var(--padding) - var(--gap));
|
|
90
146
|
|
|
91
|
-
// Last pricing column doesn't have any grid gap on the right, so
|
|
92
|
-
// override the default width specified above.
|
|
93
|
-
.is-viewport-large .table > :last-child & {
|
|
94
|
-
width: 100%;
|
|
95
147
|
}
|
|
96
148
|
}
|
|
97
149
|
}
|
|
@@ -7,6 +7,11 @@ export type PricingTableProps = {
|
|
|
7
7
|
*/
|
|
8
8
|
title: string;
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Optional header logo to display above the title.
|
|
12
|
+
*/
|
|
13
|
+
headerLogo?: ReactNode;
|
|
14
|
+
|
|
10
15
|
/**
|
|
11
16
|
* Array of items to display in the pricing table.
|
|
12
17
|
*/
|
|
@@ -38,9 +43,19 @@ export type PricingTableColumnProps = {
|
|
|
38
43
|
* Items to show in a column.
|
|
39
44
|
*/
|
|
40
45
|
children: ReactElement[];
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Additional CSS class name to apply to the column.
|
|
49
|
+
*/
|
|
50
|
+
className?: string;
|
|
41
51
|
};
|
|
42
52
|
|
|
43
53
|
export type PricingTableHeaderProps = {
|
|
54
|
+
/**
|
|
55
|
+
* Title of the header.
|
|
56
|
+
*/
|
|
57
|
+
title?: string;
|
|
58
|
+
|
|
44
59
|
/**
|
|
45
60
|
* Items to show in a header.
|
|
46
61
|
*/
|
|
@@ -29,6 +29,7 @@ const ProductPrice: FC< ProductPriceProps > = ( {
|
|
|
29
29
|
isNotConvenientPrice = false,
|
|
30
30
|
hidePriceFraction = false,
|
|
31
31
|
children,
|
|
32
|
+
variant = 'default',
|
|
32
33
|
} ) => {
|
|
33
34
|
if ( ( price == null && offPrice == null ) || ! currency ) {
|
|
34
35
|
return null;
|
|
@@ -45,6 +46,40 @@ const ProductPrice: FC< ProductPriceProps > = ( {
|
|
|
45
46
|
|
|
46
47
|
const discountElt = showDiscountLabel ? discount + __( '% off', 'jetpack-components' ) : null;
|
|
47
48
|
|
|
49
|
+
if ( variant === 'simple' ) {
|
|
50
|
+
return (
|
|
51
|
+
<div className={ styles.simple }>
|
|
52
|
+
<div className={ styles.currentPrice }>
|
|
53
|
+
<Price
|
|
54
|
+
value={ offPrice ?? price }
|
|
55
|
+
currency={ currency }
|
|
56
|
+
isOff={ ! isNotConvenientPrice }
|
|
57
|
+
hidePriceFraction={ hidePriceFraction }
|
|
58
|
+
inline={ true }
|
|
59
|
+
/>
|
|
60
|
+
<div>{ legend }</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
{ showNotOffPrice && (
|
|
64
|
+
<div className={ styles.originalPrice }>
|
|
65
|
+
<Price
|
|
66
|
+
value={ price }
|
|
67
|
+
currency={ currency }
|
|
68
|
+
isOff={ false }
|
|
69
|
+
hidePriceFraction={ hidePriceFraction }
|
|
70
|
+
inline={ true }
|
|
71
|
+
/>
|
|
72
|
+
<div>
|
|
73
|
+
{ discount &&
|
|
74
|
+
discount > 0 &&
|
|
75
|
+
discount + __( '% off the first year', 'jetpack-components' ) }
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
) }
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
48
83
|
return (
|
|
49
84
|
<>
|
|
50
85
|
<div className={ styles.container }>
|
|
@@ -11,9 +11,16 @@ import type { FC, ReactNode } from 'react';
|
|
|
11
11
|
* @param {PriceProps} props - Component props.
|
|
12
12
|
* @return {ReactNode} -Price react component.
|
|
13
13
|
*/
|
|
14
|
-
export const Price: FC< PriceProps > = ( {
|
|
14
|
+
export const Price: FC< PriceProps > = ( {
|
|
15
|
+
value,
|
|
16
|
+
currency,
|
|
17
|
+
isOff,
|
|
18
|
+
hidePriceFraction,
|
|
19
|
+
inline = false,
|
|
20
|
+
} ) => {
|
|
15
21
|
const classNames = clsx( styles.price, 'product-price_price', {
|
|
16
22
|
[ styles[ 'is-not-off-price' ] ]: ! isOff,
|
|
23
|
+
[ styles[ 'price-inline' ] ]: inline,
|
|
17
24
|
} );
|
|
18
25
|
|
|
19
26
|
const { symbol, integer, fraction } = getCurrencyObject( value, currency );
|
|
@@ -21,10 +28,19 @@ export const Price: FC< PriceProps > = ( { value, currency, isOff, hidePriceFrac
|
|
|
21
28
|
|
|
22
29
|
return (
|
|
23
30
|
<Text className={ classNames } variant="headline-medium" component="p">
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
{ inline ? (
|
|
32
|
+
<>
|
|
33
|
+
{ symbol }
|
|
34
|
+
{ integer }
|
|
35
|
+
</>
|
|
36
|
+
) : (
|
|
37
|
+
<>
|
|
38
|
+
<Text className={ styles.symbol } component="sup" variant="title-medium">
|
|
39
|
+
{ symbol }
|
|
40
|
+
</Text>
|
|
41
|
+
{ integer }
|
|
42
|
+
</>
|
|
43
|
+
) }
|
|
28
44
|
{ showPriceFraction && (
|
|
29
45
|
<Text component="sup" variant="body-small" data-testid="PriceFraction">
|
|
30
46
|
<strong>{ fraction }</strong>
|
|
@@ -75,3 +75,46 @@
|
|
|
75
75
|
.symbol {
|
|
76
76
|
font-weight: 400;
|
|
77
77
|
}
|
|
78
|
+
|
|
79
|
+
.simple {
|
|
80
|
+
display: grid;
|
|
81
|
+
grid-gap: 8px;
|
|
82
|
+
margin-bottom: 24px;
|
|
83
|
+
|
|
84
|
+
.currentPrice {
|
|
85
|
+
display: flex;
|
|
86
|
+
|
|
87
|
+
> p {
|
|
88
|
+
font-weight: 500;
|
|
89
|
+
margin-right: 8px;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
> div {
|
|
93
|
+
line-height: 30px;
|
|
94
|
+
margin-top: auto;
|
|
95
|
+
color: var(--jp-gray-50);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.originalPrice {
|
|
100
|
+
display: flex;
|
|
101
|
+
|
|
102
|
+
> p {
|
|
103
|
+
font-weight: 400;
|
|
104
|
+
margin-right: 8px;
|
|
105
|
+
color: var(--jp-gray-40);
|
|
106
|
+
font-size: 32px;
|
|
107
|
+
line-height: 50px;
|
|
108
|
+
|
|
109
|
+
&::after {
|
|
110
|
+
background: var(--jp-gray-40);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
> div {
|
|
115
|
+
line-height: 38px;
|
|
116
|
+
margin-top: auto;
|
|
117
|
+
color: var(--jp-gray-50);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -50,6 +50,11 @@ export type ProductPriceProps = {
|
|
|
50
50
|
* Alternative legend with HTML syntax
|
|
51
51
|
*/
|
|
52
52
|
children?: ReactNode;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Component variant
|
|
56
|
+
*/
|
|
57
|
+
variant?: 'default' | 'simple';
|
|
53
58
|
};
|
|
54
59
|
|
|
55
60
|
export type PriceProps = {
|
|
@@ -72,4 +77,9 @@ export type PriceProps = {
|
|
|
72
77
|
* Hides the price fraction if fraction is zero.
|
|
73
78
|
*/
|
|
74
79
|
hidePriceFraction?: boolean;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Inline layout - symbol same size and positioned inline with the number.
|
|
83
|
+
*/
|
|
84
|
+
inline?: boolean;
|
|
75
85
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/jetpack-components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Jetpack Components Package",
|
|
5
5
|
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/components/#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -41,20 +41,20 @@
|
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@automattic/format-currency": "1.0.1",
|
|
44
|
-
"@automattic/jetpack-api": "^1.0.
|
|
45
|
-
"@automattic/jetpack-boost-score-api": "^1.0.
|
|
44
|
+
"@automattic/jetpack-api": "^1.0.8",
|
|
45
|
+
"@automattic/jetpack-boost-score-api": "^1.0.10",
|
|
46
46
|
"@automattic/jetpack-script-data": "^0.5.1",
|
|
47
47
|
"@automattic/number-formatters": "^1.0.10",
|
|
48
48
|
"@babel/runtime": "^7",
|
|
49
|
-
"@wordpress/browserslist-config": "6.
|
|
50
|
-
"@wordpress/components": "30.
|
|
51
|
-
"@wordpress/compose": "7.
|
|
52
|
-
"@wordpress/data": "10.
|
|
53
|
-
"@wordpress/date": "5.
|
|
54
|
-
"@wordpress/element": "6.
|
|
55
|
-
"@wordpress/i18n": "6.
|
|
56
|
-
"@wordpress/icons": "10.
|
|
57
|
-
"@wordpress/notices": "5.
|
|
49
|
+
"@wordpress/browserslist-config": "6.28.0",
|
|
50
|
+
"@wordpress/components": "30.1.0",
|
|
51
|
+
"@wordpress/compose": "7.28.0",
|
|
52
|
+
"@wordpress/data": "10.28.0",
|
|
53
|
+
"@wordpress/date": "5.28.0",
|
|
54
|
+
"@wordpress/element": "6.28.0",
|
|
55
|
+
"@wordpress/i18n": "6.1.0",
|
|
56
|
+
"@wordpress/icons": "10.28.0",
|
|
57
|
+
"@wordpress/notices": "5.28.0",
|
|
58
58
|
"clsx": "2.1.1",
|
|
59
59
|
"prop-types": "^15.7.2",
|
|
60
60
|
"qrcode.react": "4.2.0",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"uplot-react": "1.1.4"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@automattic/jetpack-base-styles": "^1.0.
|
|
67
|
+
"@automattic/jetpack-base-styles": "^1.0.8",
|
|
68
68
|
"@babel/core": "7.28.0",
|
|
69
69
|
"@babel/preset-react": "7.27.1",
|
|
70
70
|
"@jest/globals": "30.0.4",
|