@automattic/jetpack-components 1.1.19 → 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 +5 -0
- 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/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 +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
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
|
+
|
|
5
9
|
## [1.1.19] - 2025-08-13
|
|
6
10
|
### Changed
|
|
7
11
|
- Update package dependencies. [#44701]
|
|
@@ -1504,6 +1508,7 @@
|
|
|
1504
1508
|
### Changed
|
|
1505
1509
|
- Update node version requirement to 14.16.1
|
|
1506
1510
|
|
|
1511
|
+
[1.2.0]: https://github.com/Automattic/jetpack-components/compare/1.1.19...1.2.0
|
|
1507
1512
|
[1.1.19]: https://github.com/Automattic/jetpack-components/compare/1.1.18...1.1.19
|
|
1508
1513
|
[1.1.18]: https://github.com/Automattic/jetpack-components/compare/1.1.17...1.1.18
|
|
1509
1514
|
[1.1.17]: https://github.com/Automattic/jetpack-components/compare/1.1.16...1.1.17
|
|
@@ -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
|
};
|
|
@@ -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