@automattic/jetpack-components 0.39.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/.gitattributes +8 -0
- package/CHANGELOG.md +747 -0
- package/LICENSE.txt +357 -0
- package/README.md +11 -0
- package/SECURITY.md +38 -0
- package/components/action-button/index.jsx +70 -0
- package/components/action-button/stories/index.stories.jsx +38 -0
- package/components/action-button/style.module.scss +11 -0
- package/components/action-button/test/component.jsx +16 -0
- package/components/action-popover/index.tsx +105 -0
- package/components/action-popover/stories/index.stories.jsx +115 -0
- package/components/action-popover/stories/styles.module.scss +7 -0
- package/components/action-popover/styles.module.scss +49 -0
- package/components/action-popover/types.ts +20 -0
- package/components/admin-page/index.tsx +58 -0
- package/components/admin-page/stories/index.stories.tsx +35 -0
- package/components/admin-page/stories/style.module.scss +13 -0
- package/components/admin-page/style.module.scss +11 -0
- package/components/admin-page/types.ts +41 -0
- package/components/admin-section/basic/index.tsx +15 -0
- package/components/admin-section/basic/style.module.scss +3 -0
- package/components/admin-section/hero/index.tsx +15 -0
- package/components/admin-section/hero/style.module.scss +3 -0
- package/components/admin-section/stories/index.stories.tsx +60 -0
- package/components/admin-section/types.ts +6 -0
- package/components/alert/index.tsx +61 -0
- package/components/alert/stories/index.stories.jsx +24 -0
- package/components/alert/style.module.scss +43 -0
- package/components/automattic-byline-logo/README.md +16 -0
- package/components/automattic-byline-logo/index.tsx +38 -0
- package/components/automattic-byline-logo/stories/index.stories.tsx +20 -0
- package/components/automattic-byline-logo/test/component.tsx +28 -0
- package/components/automattic-byline-logo/types.ts +18 -0
- package/components/boost-score-bar/index.tsx +103 -0
- package/components/boost-score-bar/stories/index.stories.tsx +39 -0
- package/components/boost-score-bar/style.scss +184 -0
- package/components/boost-score-bar/test/component.tsx +102 -0
- package/components/boost-score-bar/types.ts +9 -0
- package/components/button/index.tsx +84 -0
- package/components/button/stories/index.mdx +90 -0
- package/components/button/stories/index.stories.tsx +400 -0
- package/components/button/stories/style.module.scss +20 -0
- package/components/button/style.module.scss +212 -0
- package/components/button/types.ts +21 -0
- package/components/contextual-upgrade-trigger/README.md +81 -0
- package/components/contextual-upgrade-trigger/index.tsx +44 -0
- package/components/contextual-upgrade-trigger/stories/index.stories.tsx +25 -0
- package/components/contextual-upgrade-trigger/style.module.scss +75 -0
- package/components/contextual-upgrade-trigger/types.ts +9 -0
- package/components/decorative-card/index.tsx +44 -0
- package/components/decorative-card/stories/index.stories.tsx +18 -0
- package/components/decorative-card/style.scss +116 -0
- package/components/decorative-card/types.ts +16 -0
- package/components/dialog/index.tsx +69 -0
- package/components/dialog/stories/boost.png +0 -0
- package/components/dialog/stories/declaration.d.ts +1 -0
- package/components/dialog/stories/index.stories.tsx +118 -0
- package/components/dialog/stories/js-components.components.dialog.mdx +62 -0
- package/components/dialog/stories/js-components.components.dialog.stories.js +37 -0
- package/components/dialog/stories/style.module.scss +21 -0
- package/components/dialog/style.module.scss +30 -0
- package/components/donut-meter/index.tsx +147 -0
- package/components/donut-meter/stories/index.stories.tsx +51 -0
- package/components/donut-meter/style.scss +11 -0
- package/components/donut-meter/test/component.tsx +24 -0
- package/components/gridicon/index.tsx +298 -0
- package/components/gridicon/stories/index.stories.tsx +18 -0
- package/components/gridicon/style.scss +16 -0
- package/components/gridicon/types.ts +48 -0
- package/components/icon-tooltip/index.tsx +96 -0
- package/components/icon-tooltip/stories/index.stories.tsx +108 -0
- package/components/icon-tooltip/stories/style.scss +36 -0
- package/components/icon-tooltip/style.scss +121 -0
- package/components/icon-tooltip/test/component.tsx +15 -0
- package/components/icon-tooltip/types.ts +76 -0
- package/components/icons/README.md +98 -0
- package/components/icons/index.tsx +369 -0
- package/components/icons/stories/index.stories.tsx +65 -0
- package/components/icons/stories/style.module.scss +33 -0
- package/components/icons/style.module.scss +40 -0
- package/components/icons/types.ts +38 -0
- package/components/indeterminate-progress-bar/index.tsx +28 -0
- package/components/indeterminate-progress-bar/stories/index.stories.tsx +14 -0
- package/components/indeterminate-progress-bar/style.module.scss +34 -0
- package/components/indeterminate-progress-bar/types.ts +6 -0
- package/components/jetpack-footer/README.md +21 -0
- package/components/jetpack-footer/index.tsx +94 -0
- package/components/jetpack-footer/stories/index.stories.tsx +40 -0
- package/components/jetpack-footer/style.scss +116 -0
- package/components/jetpack-footer/test/component.tsx +45 -0
- package/components/jetpack-footer/types.ts +36 -0
- package/components/jetpack-logo/README.md +20 -0
- package/components/jetpack-logo/index.tsx +46 -0
- package/components/jetpack-logo/stories/index.stories.tsx +20 -0
- package/components/jetpack-logo/test/component.tsx +16 -0
- package/components/jetpack-logo/types.ts +7 -0
- package/components/jetpack-search-logo/README.md +20 -0
- package/components/jetpack-search-logo/index.tsx +93 -0
- package/components/jetpack-search-logo/stories/index.stories.tsx +21 -0
- package/components/jetpack-search-logo/test/component.tsx +16 -0
- package/components/jetpack-search-logo/types.ts +7 -0
- package/components/jetpack-vaultpress-backup-logo/README.md +19 -0
- package/components/jetpack-vaultpress-backup-logo/index.tsx +113 -0
- package/components/jetpack-vaultpress-backup-logo/stories/index.stories.tsx +20 -0
- package/components/jetpack-vaultpress-backup-logo/test/component.tsx +18 -0
- package/components/jetpack-vaultpress-backup-logo/types.ts +6 -0
- package/components/jetpack-videopress-logo/README.md +19 -0
- package/components/jetpack-videopress-logo/index.tsx +93 -0
- package/components/jetpack-videopress-logo/stories/index.stories.tsx +20 -0
- package/components/jetpack-videopress-logo/test/component.tsx +16 -0
- package/components/jetpack-videopress-logo/types.tsx +6 -0
- package/components/layout/README.md +9 -0
- package/components/layout/breakpoints.module.scss +20 -0
- package/components/layout/col/README.md +81 -0
- package/components/layout/col/index.tsx +49 -0
- package/components/layout/col/style.module.scss +28 -0
- package/components/layout/container/README.md +77 -0
- package/components/layout/container/index.tsx +42 -0
- package/components/layout/container/style.module.scss +35 -0
- package/components/layout/stories/index.stories.tsx +145 -0
- package/components/layout/stories/styles.module.scss +22 -0
- package/components/layout/types.ts +55 -0
- package/components/layout/use-breakpoint-match/README.md +43 -0
- package/components/layout/use-breakpoint-match/declaration.d.ts +9 -0
- package/components/layout/use-breakpoint-match/index.ts +79 -0
- package/components/loading-placeholder/README.md +22 -0
- package/components/loading-placeholder/index.tsx +23 -0
- package/components/loading-placeholder/stories/index.stories.tsx +78 -0
- package/components/loading-placeholder/style.module.scss +18 -0
- package/components/notice/index.tsx +98 -0
- package/components/notice/stories/index.stories.jsx +90 -0
- package/components/notice/style.module.scss +107 -0
- package/components/number-format/README.md +25 -0
- package/components/number-format/index.ts +16 -0
- package/components/pricing-card/index.tsx +127 -0
- package/components/pricing-card/stories/index.stories.tsx +55 -0
- package/components/pricing-card/style.scss +112 -0
- package/components/pricing-card/test/component.tsx +89 -0
- package/components/pricing-card/types.ts +52 -0
- package/components/pricing-slider/index.tsx +53 -0
- package/components/pricing-slider/stories/index.stories.tsx +44 -0
- package/components/pricing-slider/style.scss +55 -0
- package/components/pricing-slider/test/component.tsx +14 -0
- package/components/pricing-slider/types.ts +58 -0
- package/components/pricing-table/index.tsx +200 -0
- package/components/pricing-table/stories/index.stories.tsx +74 -0
- package/components/pricing-table/styles.module.scss +160 -0
- package/components/pricing-table/test/component.tsx +41 -0
- package/components/pricing-table/types.ts +84 -0
- package/components/product-offer/icons-card.tsx +47 -0
- package/components/product-offer/index.tsx +115 -0
- package/components/product-offer/product-offer-header.tsx +23 -0
- package/components/product-offer/stories/index.stories.tsx +104 -0
- package/components/product-offer/style.module.scss +93 -0
- package/components/product-offer/types.ts +125 -0
- package/components/product-price/index.tsx +90 -0
- package/components/product-price/price.tsx +35 -0
- package/components/product-price/stories/index.stories.tsx +33 -0
- package/components/product-price/style.module.scss +69 -0
- package/components/product-price/test/component.tsx +26 -0
- package/components/product-price/types.ts +73 -0
- package/components/qr-code/Readme.md +5 -0
- package/components/qr-code/index.tsx +78 -0
- package/components/qr-code/stories/index.stories.tsx +32 -0
- package/components/record-meter-bar/index.tsx +135 -0
- package/components/record-meter-bar/stories/index.stories.tsx +39 -0
- package/components/record-meter-bar/style.scss +38 -0
- package/components/record-meter-bar/test/component.tsx +52 -0
- package/components/spinner/README.md +28 -0
- package/components/spinner/index.jsx +45 -0
- package/components/spinner/stories/index.stories.jsx +20 -0
- package/components/spinner/style.scss +31 -0
- package/components/spinner/test/component.jsx +17 -0
- package/components/split-button/index.tsx +39 -0
- package/components/split-button/stories/index.stories.tsx +35 -0
- package/components/split-button/style.module.scss +21 -0
- package/components/split-button/types.ts +14 -0
- package/components/stat-card/index.tsx +49 -0
- package/components/stat-card/stories/index.mdx +35 -0
- package/components/stat-card/stories/index.stories.tsx +41 -0
- package/components/stat-card/style.module.scss +49 -0
- package/components/stat-card/test/component.tsx +24 -0
- package/components/stat-card/types.ts +28 -0
- package/components/status/README.md +42 -0
- package/components/status/index.tsx +40 -0
- package/components/status/stories/index.stories.tsx +44 -0
- package/components/status/style.module.scss +72 -0
- package/components/terms-of-service/index.tsx +63 -0
- package/components/terms-of-service/styles.scss +10 -0
- package/components/terms-of-service/test/index.test.jsx +52 -0
- package/components/terms-of-service/types.ts +30 -0
- package/components/testimonials/index.tsx +58 -0
- package/components/testimonials/style.scss +127 -0
- package/components/testimonials/test/component.tsx +103 -0
- package/components/testimonials/testimonial.tsx +47 -0
- package/components/testimonials/types.ts +17 -0
- package/components/text/constants.ts +30 -0
- package/components/text/index.tsx +91 -0
- package/components/text/stories/headings.index.stories.tsx +80 -0
- package/components/text/stories/index.mdx +195 -0
- package/components/text/stories/index.stories.tsx +69 -0
- package/components/text/stories/style.module.scss +64 -0
- package/components/text/stories/title.index.stories.tsx +34 -0
- package/components/text/style.module.scss +125 -0
- package/components/text/types.ts +53 -0
- package/components/theme-provider/Readme.md +49 -0
- package/components/theme-provider/globals.module.scss +6 -0
- package/components/theme-provider/index.tsx +151 -0
- package/components/theme-provider/stories/index.stories.tsx +164 -0
- package/components/theme-provider/stories/style.module.scss +79 -0
- package/components/theme-provider/types.ts +25 -0
- package/components/toggle-control/README.md +5 -0
- package/components/toggle-control/index.tsx +67 -0
- package/components/toggle-control/stories/index.stories.tsx +83 -0
- package/components/toggle-control/styles.module.scss +41 -0
- package/components/zendesk-chat/README.md +26 -0
- package/components/zendesk-chat/constants.ts +2 -0
- package/components/zendesk-chat/index.tsx +22 -0
- package/components/zendesk-chat/stories/index.stories.tsx +15 -0
- package/components/zendesk-chat/test/component.tsx +16 -0
- package/components/zendesk-chat/types.ts +3 -0
- package/index.ts +69 -0
- package/lib/locale/index.ts +48 -0
- package/lib/locale/test/index.ts +21 -0
- package/package.json +80 -0
- package/tools/get-product-checkout-url/Readme.md +32 -0
- package/tools/get-product-checkout-url/index.ts +31 -0
- package/tools/jp-redirect/README.md +61 -0
- package/tools/jp-redirect/index.ts +62 -0
- package/tools/jp-redirect/test/index.tsx +89 -0
- package/tools/jp-redirect/types.ts +49 -0
- package/tools/pricing-utils/README.md +16 -0
- package/tools/pricing-utils/index.ts +11 -0
- package/tools/pricing-utils/test/index.ts +39 -0
- package/tools/pricing-utils/types.ts +12 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import ZendeskChat from '../index';
|
|
4
|
+
|
|
5
|
+
describe( 'ZendeskChat', () => {
|
|
6
|
+
afterEach( () => {
|
|
7
|
+
// restore the spy created with spyOn
|
|
8
|
+
jest.restoreAllMocks();
|
|
9
|
+
} );
|
|
10
|
+
|
|
11
|
+
it( 'renders the zendesk chat widget', () => {
|
|
12
|
+
render( <ZendeskChat /> );
|
|
13
|
+
|
|
14
|
+
expect( screen.getByTestId( 'zendesk-chat-container' ) ).toBeInTheDocument();
|
|
15
|
+
} );
|
|
16
|
+
} );
|
package/index.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This program is free software; you can redistribute it and/or
|
|
3
|
+
modify it under the terms of the GNU General Public License
|
|
4
|
+
as published by the Free Software Foundation; either version 2
|
|
5
|
+
of the License, or (at your option) any later version.
|
|
6
|
+
|
|
7
|
+
This program is distributed in the hope that it will be useful,
|
|
8
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
+
GNU General Public License for more details.
|
|
11
|
+
|
|
12
|
+
You should have received a copy of the GNU General Public License
|
|
13
|
+
along with this program; if not, write to the Free Software
|
|
14
|
+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export { default as JetpackLogo } from './components/jetpack-logo';
|
|
18
|
+
export { default as JetpackSearchLogo } from './components/jetpack-search-logo';
|
|
19
|
+
export { default as JetpackVaultPressBackupLogo } from './components/jetpack-vaultpress-backup-logo';
|
|
20
|
+
export { default as JetpackVideoPressLogo } from './components/jetpack-videopress-logo';
|
|
21
|
+
export { default as getRedirectUrl } from './tools/jp-redirect';
|
|
22
|
+
export { default as getProductCheckoutUrl } from './tools/get-product-checkout-url';
|
|
23
|
+
export { isFirstMonthTrial } from './tools/pricing-utils';
|
|
24
|
+
export { default as AutomatticBylineLogo } from './components/automattic-byline-logo';
|
|
25
|
+
export { default as JetpackFooter } from './components/jetpack-footer';
|
|
26
|
+
export { default as Spinner } from './components/spinner';
|
|
27
|
+
export { default as Gridicon } from './components/gridicon';
|
|
28
|
+
export { default as IconTooltip } from './components/icon-tooltip';
|
|
29
|
+
export { default as ActionButton } from './components/action-button';
|
|
30
|
+
export { default as PricingCard } from './components/pricing-card';
|
|
31
|
+
export { default as PricingSlider } from './components/pricing-slider';
|
|
32
|
+
export { default as AdminSection } from './components/admin-section/basic';
|
|
33
|
+
export { default as AdminSectionHero } from './components/admin-section/hero';
|
|
34
|
+
export { default as AdminPage } from './components/admin-page';
|
|
35
|
+
export { default as DecorativeCard } from './components/decorative-card';
|
|
36
|
+
export { default as Col } from './components/layout/col';
|
|
37
|
+
export { default as Testimonials } from './components/testimonials';
|
|
38
|
+
export { default as Container } from './components/layout/container';
|
|
39
|
+
export { default as useBreakpointMatch } from './components/layout/use-breakpoint-match';
|
|
40
|
+
export * from './components/icons';
|
|
41
|
+
export { default as SplitButton } from './components/split-button';
|
|
42
|
+
export { default as ThemeProvider } from './components/theme-provider';
|
|
43
|
+
export { default as Text, H2, H3, Title } from './components/text';
|
|
44
|
+
export { default as ToggleControl } from './components/toggle-control';
|
|
45
|
+
export { default as numberFormat } from './components/number-format';
|
|
46
|
+
export { default as QRCode } from './components/qr-code';
|
|
47
|
+
export { default as Button } from './components/button';
|
|
48
|
+
export { default as LoadingPlaceholder } from './components/loading-placeholder';
|
|
49
|
+
export { default as TermsOfService } from './components/terms-of-service';
|
|
50
|
+
export {
|
|
51
|
+
default as PricingTable,
|
|
52
|
+
PricingTableColumn,
|
|
53
|
+
PricingTableHeader,
|
|
54
|
+
PricingTableItem,
|
|
55
|
+
} from './components/pricing-table';
|
|
56
|
+
export { default as BoostScoreBar } from './components/boost-score-bar';
|
|
57
|
+
export { default as ProductPrice } from './components/product-price';
|
|
58
|
+
export { default as ProductOffer, IconsCard } from './components/product-offer';
|
|
59
|
+
export { default as Dialog } from './components/dialog';
|
|
60
|
+
export { default as DonutMeter } from './components/donut-meter';
|
|
61
|
+
export { default as RecordMeterBar } from './components/record-meter-bar';
|
|
62
|
+
export { default as ContextualUpgradeTrigger } from './components/contextual-upgrade-trigger';
|
|
63
|
+
export { default as Alert } from './components/alert';
|
|
64
|
+
export { default as Notice } from './components/notice';
|
|
65
|
+
export { default as Status } from './components/status';
|
|
66
|
+
export { default as IndeterminateProgressBar } from './components/indeterminate-progress-bar';
|
|
67
|
+
export { default as ActionPopover } from './components/action-popover';
|
|
68
|
+
export { default as ZendeskChat } from './components/zendesk-chat';
|
|
69
|
+
export { getUserLocale, cleanLocale } from './lib/locale';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { getSettings } from '@wordpress/date';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Clean up WP locale so it matches the format expected by browsers.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} locale - Locale given by WordPress.
|
|
7
|
+
* @returns {string} Browser-formatted locale.
|
|
8
|
+
*/
|
|
9
|
+
export const cleanLocale = ( locale: string ) => {
|
|
10
|
+
const regex = /^([a-z]{2,3})(_[a-z]{2}|_[a-z][a-z0-9]{4,7})?(?:_.*)?$/i;
|
|
11
|
+
|
|
12
|
+
// Search for the correct locale format:
|
|
13
|
+
// e.g. af, arq, fr_FR, pap_CW, de_DE_formal, art_xpirate
|
|
14
|
+
const localeRegex = locale.match( regex );
|
|
15
|
+
|
|
16
|
+
// Locale was set to something that seems invalid, fallback to en-US.
|
|
17
|
+
if ( ! localeRegex ) {
|
|
18
|
+
return 'en-US';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
// Keep only the language and the region, and replace the underscore used in WP locale by an hyphen.
|
|
23
|
+
`${ localeRegex[ 1 ] }${ localeRegex[ 2 ] ? localeRegex[ 2 ] : '' }`.replace( '_', '-' )
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Since global is used inside getUserLocale, we need to declare it for TS
|
|
28
|
+
declare const global: typeof globalThis;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Current user locale, or browser locale as fallback.
|
|
32
|
+
*
|
|
33
|
+
* @returns {string} Formatted user locale (e.g. `en-US` or `fr-FR`).
|
|
34
|
+
*/
|
|
35
|
+
export const getUserLocale = () => {
|
|
36
|
+
const {
|
|
37
|
+
l10n: { locale },
|
|
38
|
+
} = getSettings();
|
|
39
|
+
|
|
40
|
+
if ( locale ) {
|
|
41
|
+
return cleanLocale( locale );
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Fallback to the browser locale if necessary.
|
|
45
|
+
const language = global?.window?.navigator?.language ?? 'en-US';
|
|
46
|
+
|
|
47
|
+
return language;
|
|
48
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { cleanLocale } from '../';
|
|
2
|
+
|
|
3
|
+
describe( 'cleanLocale', () => {
|
|
4
|
+
const testLocales = [
|
|
5
|
+
[ 'af', 'af' ],
|
|
6
|
+
[ 'arq', 'arq' ],
|
|
7
|
+
[ 'fr_FR', 'fr-FR' ],
|
|
8
|
+
[ 'pap_CW', 'pap-CW' ],
|
|
9
|
+
[ 'de_DE_formal', 'de-DE' ],
|
|
10
|
+
[ 'art_xpirate', 'art-xpirate' ],
|
|
11
|
+
[ 'art_xemoji', 'art-xemoji' ],
|
|
12
|
+
[ 'pt_PT_ao90', 'pt-PT' ],
|
|
13
|
+
[ 'deDE', 'en-US' ],
|
|
14
|
+
[ 'foobarde_DE', 'en-US' ], // Language should never be more than 3 chars long.
|
|
15
|
+
[ 'en_alotofchars', 'en' ], // region or variant tags should not be more than 8 chars.
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
it.each( testLocales )( '%s is cleaned into %s', ( input, expected ) => {
|
|
19
|
+
expect( cleanLocale( input ) ).toBe( expected );
|
|
20
|
+
} );
|
|
21
|
+
} );
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@automattic/jetpack-components",
|
|
3
|
+
"version": "0.39.0",
|
|
4
|
+
"description": "Jetpack Components Package",
|
|
5
|
+
"author": "Automattic",
|
|
6
|
+
"license": "GPL-2.0-or-later",
|
|
7
|
+
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/components/#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/Automattic/jetpack.git",
|
|
11
|
+
"directory": "projects/js-packages/components"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/Automattic/jetpack/labels/[JS Package] Components"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@automattic/format-currency": "1.0.1",
|
|
18
|
+
"@babel/runtime": "^7",
|
|
19
|
+
"@wordpress/browserslist-config": "5.18.0",
|
|
20
|
+
"@wordpress/components": "25.1.0",
|
|
21
|
+
"@wordpress/compose": "6.12.0",
|
|
22
|
+
"@wordpress/date": "4.35.0",
|
|
23
|
+
"@wordpress/element": "5.12.0",
|
|
24
|
+
"@wordpress/i18n": "4.35.0",
|
|
25
|
+
"@wordpress/icons": "9.26.0",
|
|
26
|
+
"classnames": "2.3.2",
|
|
27
|
+
"prop-types": "^15.7.2",
|
|
28
|
+
"qrcode.react": "3.1.0",
|
|
29
|
+
"react-slider": "2.0.5"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@automattic/jetpack-base-styles": "^0.6.1",
|
|
33
|
+
"@babel/core": "7.21.5",
|
|
34
|
+
"@babel/preset-react": "7.18.6",
|
|
35
|
+
"@jest/globals": "29.4.3",
|
|
36
|
+
"@storybook/addon-actions": "7.0.5",
|
|
37
|
+
"@storybook/blocks": "7.0.5",
|
|
38
|
+
"@storybook/react": "7.0.5",
|
|
39
|
+
"@testing-library/dom": "8.19.1",
|
|
40
|
+
"@testing-library/react": "13.4.0",
|
|
41
|
+
"@testing-library/user-event": "14.4.3",
|
|
42
|
+
"@types/jest": "29.2.5",
|
|
43
|
+
"@types/qrcode.react": "1.0.2",
|
|
44
|
+
"@types/react": "18.0.27",
|
|
45
|
+
"@types/react-dom": "18.0.10",
|
|
46
|
+
"@types/react-slider": "1.3.1",
|
|
47
|
+
"@types/react-test-renderer": "18.0.0",
|
|
48
|
+
"@types/testing-library__jest-dom": "5.14.1",
|
|
49
|
+
"jest": "29.4.3",
|
|
50
|
+
"jest-environment-jsdom": "29.4.3",
|
|
51
|
+
"react": "18.2.0",
|
|
52
|
+
"react-dom": "18.2.0",
|
|
53
|
+
"react-test-renderer": "18.2.0",
|
|
54
|
+
"require-from-string": "2.0.2",
|
|
55
|
+
"resize-observer-polyfill": "1.5.1",
|
|
56
|
+
"typescript": "5.0.4",
|
|
57
|
+
"webpack": "5.76.0",
|
|
58
|
+
"webpack-cli": "4.9.1"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"react": "^18.0.0",
|
|
62
|
+
"react-dom": "^18.0.0"
|
|
63
|
+
},
|
|
64
|
+
"type": "module",
|
|
65
|
+
"browserslist": [
|
|
66
|
+
"extends @wordpress/browserslist-config"
|
|
67
|
+
],
|
|
68
|
+
"exports": {
|
|
69
|
+
".": "./index.ts"
|
|
70
|
+
},
|
|
71
|
+
"sideEffects": [
|
|
72
|
+
"*.css",
|
|
73
|
+
"*.scss"
|
|
74
|
+
],
|
|
75
|
+
"scripts": {
|
|
76
|
+
"build": "pnpm run compile-ts",
|
|
77
|
+
"compile-ts": "tsc --pretty",
|
|
78
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# getProductCheckoutUrl
|
|
2
|
+
|
|
3
|
+
Helper function useful to build the product checkout URL.
|
|
4
|
+
|
|
5
|
+
## API
|
|
6
|
+
|
|
7
|
+
The function expects the following params:
|
|
8
|
+
|
|
9
|
+
### productSlug
|
|
10
|
+
This is the WordPress.com product slug.
|
|
11
|
+
For instance, for security bundle it's usually defined with `jetpack_security_t1_yearly`.
|
|
12
|
+
|
|
13
|
+
### siteSuffix
|
|
14
|
+
Site slug suffix to be used as part of Calypso URLs. Take adventage of [get_site_suffix](../../../../packages/status/src/class-status.php#L327) backend helper
|
|
15
|
+
|
|
16
|
+
### redirectUrl
|
|
17
|
+
The URL to redirect to after checkout.
|
|
18
|
+
|
|
19
|
+
### isUserConnected
|
|
20
|
+
Boolean value that defined whether the user is connected, or not.
|
|
21
|
+
It will affect the built URL by adding the unlinked param to the query string.
|
|
22
|
+
|
|
23
|
+
```jsx
|
|
24
|
+
import { getProductCheckoutUrl } from '@automattic/jetpack-components';
|
|
25
|
+
|
|
26
|
+
const checkoutUrl = useProductCheckoutWorkflow(
|
|
27
|
+
'jetpack_security_t1_yearly',
|
|
28
|
+
'poweredsite.wordpress.com',
|
|
29
|
+
'https://poweredsite.wordpress.com/wp-admin/admin.php?page=jetpack-protect',
|
|
30
|
+
true
|
|
31
|
+
);
|
|
32
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return the checkout URL for the given product.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} productSlug - wpcom product slug.
|
|
5
|
+
* @param {string} siteSuffix - Site suffix
|
|
6
|
+
* @param {string} redirectUrl - Redirect URL used to define redirect_to
|
|
7
|
+
* @param {boolean} isUserConnected - True when the user is connected Jetpack
|
|
8
|
+
* @returns {string} The Calypso checkout URL
|
|
9
|
+
*/
|
|
10
|
+
export default function getProductCheckoutUrl(
|
|
11
|
+
productSlug: string,
|
|
12
|
+
siteSuffix: string,
|
|
13
|
+
redirectUrl: string,
|
|
14
|
+
isUserConnected: boolean
|
|
15
|
+
) {
|
|
16
|
+
const checkoutUrl = new URL( 'https://wordpress.com/checkout/' );
|
|
17
|
+
const checkoutProductUrl = new URL( `${ checkoutUrl }${ siteSuffix }/${ productSlug }` );
|
|
18
|
+
|
|
19
|
+
// Add redirect_to parameter
|
|
20
|
+
checkoutProductUrl.searchParams.set( 'redirect_to', redirectUrl );
|
|
21
|
+
|
|
22
|
+
// Add unlimited when user is not connected to Jetpack.
|
|
23
|
+
if ( ! isUserConnected ) {
|
|
24
|
+
checkoutProductUrl.searchParams.set( 'unlinked', '1' );
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Add site to query string.
|
|
28
|
+
checkoutProductUrl.searchParams.set( 'site', siteSuffix );
|
|
29
|
+
|
|
30
|
+
return checkoutProductUrl.toString();
|
|
31
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Jetpack Redirects
|
|
2
|
+
|
|
3
|
+
A helper function to build URLs using the `jetpack.com/redirect` service.
|
|
4
|
+
|
|
5
|
+
If you are an automattician, refer to PCYsg-pY7-p2 for all related information and links to our internal tools.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```jsx
|
|
10
|
+
import { getRedirectUrl } from '@automattic/jetpack-components';
|
|
11
|
+
getRedirectUrl( source, args );
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### source (required)
|
|
15
|
+
|
|
16
|
+
Source can be either a “source handler” or an URL.
|
|
17
|
+
|
|
18
|
+
A “source handler” must be registered in the Jetpack Redirects service, on the server side. It’s a slug that points to an URL that may or may not have dynamic parts in it.
|
|
19
|
+
|
|
20
|
+
An “URL” is a string that must start with “https://" and doesn’t need to be registered on the server. However, if it is registered, it will point to the URL set as target there rather than to the source. (Note: It will only work for whitelisted domains, unless the URL is explicitly registered on the server)
|
|
21
|
+
|
|
22
|
+
### args (optional)
|
|
23
|
+
|
|
24
|
+
This is optional and allows you to pass an object with more parameters to build the URL.
|
|
25
|
+
|
|
26
|
+
This is not a complete list as any argument passed here will be sent to as a query parameter to the Redirect server. These parameters will not necessarily be passed over to the final destination URL. If you want to add a parameter to the final destination URL, use the `query` argument.
|
|
27
|
+
|
|
28
|
+
* **site**: Optional (but recommended). This is used to identify the site and also to fill in the `[site]` placeholder in the target. Will default to the value of `jetpack_redirects.currentSiteRawUrl`, if available.
|
|
29
|
+
|
|
30
|
+
* **path**: Optional. Used to fill in the `[path]` placeholder in the target.
|
|
31
|
+
|
|
32
|
+
* **query**: Optional. A string with additional variables to be added in the query string. Must be passed as a string in `key=value&foo=bar` format.
|
|
33
|
+
|
|
34
|
+
* **anchor**: Optional. An anchor to be added to the final URL. Must be a single string. Example: `section1`
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
### Example 1
|
|
39
|
+
|
|
40
|
+
`getRedirectUrl( 'jetpack', { query: 'foo=bar', anchor: 'section' } );`
|
|
41
|
+
|
|
42
|
+
This will return the following URL: `https://jetpack.com/redirect?source=jetpack&anchor=section&query=foo%3Dbar`
|
|
43
|
+
|
|
44
|
+
When accessing this URL, the user will be redirected to: `https://jetpack.com/?foo=bar#section`
|
|
45
|
+
|
|
46
|
+
### Example 2 (placeholders):
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
getRedirectUrl(
|
|
50
|
+
'calypso-edit-post',
|
|
51
|
+
{
|
|
52
|
+
path: '1234',
|
|
53
|
+
site: 'example.org'
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
```
|
|
57
|
+
This will return the following URL: `https://jetpack.com/redirect?site=example.org&source=calypso-edit-post&path=1234`
|
|
58
|
+
|
|
59
|
+
The `calypso-edit-post` source is registered on the server and points to `https://wordpress.com/post/[site]/[path]`, so the final URL that the user will be redirected to is:
|
|
60
|
+
|
|
61
|
+
`https://wordpress.com/post/example.org/1234`
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/* global jetpack_redirects */
|
|
2
|
+
|
|
3
|
+
import { GetRedirectUrlArgs, QueryVars } from './types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Builds an URL using the jetpack.com/redirect/ service
|
|
7
|
+
*
|
|
8
|
+
* If source is a simple slug, it will be sent using the source query parameter. e.g. jetpack.com/redirect/?source=slug
|
|
9
|
+
*
|
|
10
|
+
* If source is a full URL, starting with https://, it will be sent using the url query parameter. e.g. jetpack.com/redirect/?url=https://wordpress.com
|
|
11
|
+
*
|
|
12
|
+
* Note: if using full URL, query parameters and anchor must be passed in args. Any querystring of url fragment in the URL will be discarded.
|
|
13
|
+
*
|
|
14
|
+
* @since 0.2.0
|
|
15
|
+
* @param {string} source - The URL handler registered in the server or the full destination URL (starting with https://).
|
|
16
|
+
* @param {GetRedirectUrlArgs} args - Additional arguments to build the url.
|
|
17
|
+
* This is not a complete list as any argument passed here
|
|
18
|
+
* will be sent to as a query parameter to the Redirect server.
|
|
19
|
+
* These parameters will not necessarily be passed over to the final destination URL.
|
|
20
|
+
* If you want to add a parameter to the final destination URL, use the `query` argument.
|
|
21
|
+
* @returns {string} The redirect URL
|
|
22
|
+
*/
|
|
23
|
+
export default function getRedirectUrl( source: string, args: GetRedirectUrlArgs = {} ) {
|
|
24
|
+
const queryVars: QueryVars = {};
|
|
25
|
+
|
|
26
|
+
let calypsoEnv;
|
|
27
|
+
if ( typeof window !== 'undefined' ) {
|
|
28
|
+
calypsoEnv = window.Initial_State?.calypsoEnv;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if ( source.search( 'https://' ) === 0 ) {
|
|
32
|
+
const parsedUrl = new URL( source );
|
|
33
|
+
|
|
34
|
+
// discard any query and fragments.
|
|
35
|
+
source = `https://${ parsedUrl.host }${ parsedUrl.pathname }`;
|
|
36
|
+
queryVars.url = encodeURIComponent( source );
|
|
37
|
+
} else {
|
|
38
|
+
queryVars.source = encodeURIComponent( source );
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Object.keys( args ).map( argName => {
|
|
42
|
+
queryVars[ argName ] = encodeURIComponent( args[ argName ] );
|
|
43
|
+
} );
|
|
44
|
+
|
|
45
|
+
if (
|
|
46
|
+
! Object.keys( queryVars ).includes( 'site' ) &&
|
|
47
|
+
typeof jetpack_redirects !== 'undefined' &&
|
|
48
|
+
jetpack_redirects.hasOwnProperty( 'currentSiteRawUrl' )
|
|
49
|
+
) {
|
|
50
|
+
queryVars.site = jetpack_redirects.currentSiteRawUrl;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if ( calypsoEnv ) {
|
|
54
|
+
queryVars.calypso_env = calypsoEnv;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const queryString = Object.keys( queryVars )
|
|
58
|
+
.map( key => key + '=' + queryVars[ key ] )
|
|
59
|
+
.join( '&' );
|
|
60
|
+
|
|
61
|
+
return `https://jetpack.com/redirect/?` + queryString;
|
|
62
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import getRedirectUrl from '../';
|
|
2
|
+
|
|
3
|
+
describe( 'getRedirectUrl', () => {
|
|
4
|
+
it( 'simple url', () => {
|
|
5
|
+
const url = getRedirectUrl( 'simple' );
|
|
6
|
+
const parsedUrl = new URL( url );
|
|
7
|
+
|
|
8
|
+
expect( parsedUrl.searchParams.get( 'source' ) ).toBe( 'simple' );
|
|
9
|
+
} );
|
|
10
|
+
|
|
11
|
+
it( 'Random param', () => {
|
|
12
|
+
const url = getRedirectUrl( 'simple', { random: 'asd' } );
|
|
13
|
+
expect( url ).toBe( 'https://jetpack.com/redirect/?source=simple&random=asd' );
|
|
14
|
+
} );
|
|
15
|
+
|
|
16
|
+
it( 'Test path', () => {
|
|
17
|
+
const url = getRedirectUrl( 'simple', { path: '1234' } );
|
|
18
|
+
const parsedUrl = new URL( url );
|
|
19
|
+
|
|
20
|
+
expect( parsedUrl.searchParams.get( 'source' ) ).toBe( 'simple' );
|
|
21
|
+
expect( parsedUrl.searchParams.get( 'path' ) ).toBe( '1234' );
|
|
22
|
+
} );
|
|
23
|
+
|
|
24
|
+
it( 'Test path with special chars', () => {
|
|
25
|
+
const url = getRedirectUrl( 'simple', { path: 'weird value!' } );
|
|
26
|
+
const parsedUrl = new URL( url );
|
|
27
|
+
|
|
28
|
+
expect( parsedUrl.searchParams.get( 'source' ) ).toBe( 'simple' );
|
|
29
|
+
expect( parsedUrl.searchParams.get( 'path' ) ).toBe( 'weird value!' );
|
|
30
|
+
} );
|
|
31
|
+
|
|
32
|
+
it( 'Test query', () => {
|
|
33
|
+
const url = getRedirectUrl( 'simple', { query: 'key=1234&other=super' } );
|
|
34
|
+
const parsedUrl = new URL( url );
|
|
35
|
+
|
|
36
|
+
expect( parsedUrl.searchParams.get( 'source' ) ).toBe( 'simple' );
|
|
37
|
+
expect( parsedUrl.searchParams.get( 'query' ) ).toBe( 'key=1234&other=super' );
|
|
38
|
+
} );
|
|
39
|
+
|
|
40
|
+
it( 'Test anchor', () => {
|
|
41
|
+
const url = getRedirectUrl( 'simple', { anchor: 'section' } );
|
|
42
|
+
const parsedUrl = new URL( url );
|
|
43
|
+
|
|
44
|
+
expect( parsedUrl.searchParams.get( 'source' ) ).toBe( 'simple' );
|
|
45
|
+
expect( parsedUrl.searchParams.get( 'anchor' ) ).toBe( 'section' );
|
|
46
|
+
} );
|
|
47
|
+
|
|
48
|
+
it( 'Test all', () => {
|
|
49
|
+
const url = getRedirectUrl( 'simple', {
|
|
50
|
+
query: 'key=1234&other=super',
|
|
51
|
+
anchor: 'section',
|
|
52
|
+
site: 'example.org',
|
|
53
|
+
path: '123',
|
|
54
|
+
} );
|
|
55
|
+
const parsedUrl = new URL( url );
|
|
56
|
+
|
|
57
|
+
expect( parsedUrl.searchParams.get( 'source' ) ).toBe( 'simple' );
|
|
58
|
+
expect( parsedUrl.searchParams.get( 'anchor' ) ).toBe( 'section' );
|
|
59
|
+
expect( parsedUrl.searchParams.get( 'query' ) ).toBe( 'key=1234&other=super' );
|
|
60
|
+
expect( parsedUrl.searchParams.get( 'site' ) ).toBe( 'example.org' );
|
|
61
|
+
expect( parsedUrl.searchParams.get( 'path' ) ).toBe( '123' );
|
|
62
|
+
} );
|
|
63
|
+
|
|
64
|
+
it( 'Test passing an URL as the first parameter', () => {
|
|
65
|
+
const url = getRedirectUrl( 'https://wordpress.com/support' );
|
|
66
|
+
const value = encodeURIComponent( 'https://wordpress.com/support' );
|
|
67
|
+
expect( url ).toBe( `https://jetpack.com/redirect/?url=${ value }` );
|
|
68
|
+
} );
|
|
69
|
+
|
|
70
|
+
it( 'Test passing an URL as the first parameter and query', () => {
|
|
71
|
+
const url = getRedirectUrl( 'https://wordpress.com/support', {
|
|
72
|
+
query: 'key=1234&other=super',
|
|
73
|
+
} );
|
|
74
|
+
const parsedUrl = new URL( url );
|
|
75
|
+
|
|
76
|
+
expect( parsedUrl.searchParams.get( 'url' ) ).toBe( 'https://wordpress.com/support' );
|
|
77
|
+
expect( parsedUrl.searchParams.get( 'query' ) ).toBe( 'key=1234&other=super' );
|
|
78
|
+
} );
|
|
79
|
+
|
|
80
|
+
it( 'Test passing an URL as the first parameter and query discarding info from url', () => {
|
|
81
|
+
const url = getRedirectUrl( 'https://wordpress.com/support?super=mega&key=value#section1', {
|
|
82
|
+
query: 'key=1234&other=super',
|
|
83
|
+
} );
|
|
84
|
+
const parsedUrl = new URL( url );
|
|
85
|
+
|
|
86
|
+
expect( parsedUrl.searchParams.get( 'url' ) ).toBe( 'https://wordpress.com/support' );
|
|
87
|
+
expect( parsedUrl.searchParams.get( 'query' ) ).toBe( 'key=1234&other=super' );
|
|
88
|
+
} );
|
|
89
|
+
} );
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export type GetRedirectUrlArgs = {
|
|
2
|
+
/**
|
|
3
|
+
* URL of the current site. Will default to the value of
|
|
4
|
+
* jetpack_redirects.currentSiteRawUrl, if available.
|
|
5
|
+
* Used to fill in the `[site]` placeholder in the target.
|
|
6
|
+
*/
|
|
7
|
+
site?: string;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Additional path to be appended to the URL.
|
|
11
|
+
* Used to fill in the `[path]` placeholder in the target.
|
|
12
|
+
*/
|
|
13
|
+
path?: string;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Query parameters to be added to the final destination URL.
|
|
17
|
+
* Should be in query string format (e.g. 'key=value&foo=bar').
|
|
18
|
+
*/
|
|
19
|
+
query?: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Anchor to be added to the URL. Must be a single string.
|
|
23
|
+
* Example: `section1`.
|
|
24
|
+
*/
|
|
25
|
+
anchor?: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Any other custom arguments to be added to the final destination URL.
|
|
29
|
+
*/
|
|
30
|
+
[ key: string ]: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type QueryVars = {
|
|
34
|
+
url?: string;
|
|
35
|
+
site?: string;
|
|
36
|
+
source?: string;
|
|
37
|
+
calypso_env?: string;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
declare global {
|
|
41
|
+
interface Window {
|
|
42
|
+
Initial_State: {
|
|
43
|
+
calypsoEnv?: string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const jetpack_redirects: {
|
|
47
|
+
currentSiteRawUrl?: string;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Pricing Utils
|
|
2
|
+
|
|
3
|
+
This is a helper folder that contains util functions related to pricing (e.g. Intro Offers) and can be extended to include other methods.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
### isFirstMonthTrial
|
|
8
|
+
|
|
9
|
+
```jsx
|
|
10
|
+
import { isFirstMonthTrial } from '@automattic/jetpack-components';
|
|
11
|
+
isFirstMonthTrial( introOffer );
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
#### introOffer (required)
|
|
15
|
+
|
|
16
|
+
An Intro Offer object returned by the API. It must contain an `interval_unit` and `interval_count` for the function to check if it's a first month trial or not. It returns `true` if `interval_unit` is `'month'` **and** `interval_count` is `1`, all other combinations return `false`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { IntroOffer } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns whether an Introductory Offer is a first month trial
|
|
5
|
+
*
|
|
6
|
+
* @param {IntroOffer} introOffer - an intro offer object
|
|
7
|
+
* @returns {boolean} Whether it's a first month trial or not
|
|
8
|
+
*/
|
|
9
|
+
export function isFirstMonthTrial( introOffer: IntroOffer ): boolean {
|
|
10
|
+
return introOffer?.interval_count === 1 && introOffer?.interval_unit === 'month';
|
|
11
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isFirstMonthTrial } from '..';
|
|
2
|
+
import { IntroOffer } from './../types';
|
|
3
|
+
|
|
4
|
+
const trialIntroOffer: IntroOffer = {
|
|
5
|
+
product_id: 0,
|
|
6
|
+
product_slug: 'test_product',
|
|
7
|
+
currency_code: 'USD',
|
|
8
|
+
formatted_price: '$10',
|
|
9
|
+
original_price: 10,
|
|
10
|
+
raw_price: 1,
|
|
11
|
+
discount_percentage: 90,
|
|
12
|
+
ineligible_reason: null,
|
|
13
|
+
interval_unit: 'month',
|
|
14
|
+
interval_count: 1,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe( 'isFirstMonthTrial', () => {
|
|
18
|
+
it( 'returns true if interval_unit is "month" and interval_count is 1', () => {
|
|
19
|
+
expect( isFirstMonthTrial( trialIntroOffer ) ).toBe( true );
|
|
20
|
+
} );
|
|
21
|
+
|
|
22
|
+
it( 'returns false if interval_unit is not "month"', () => {
|
|
23
|
+
const introOffer: IntroOffer = {
|
|
24
|
+
...trialIntroOffer,
|
|
25
|
+
interval_unit: 'year',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
expect( isFirstMonthTrial( introOffer ) ).toBe( false );
|
|
29
|
+
} );
|
|
30
|
+
|
|
31
|
+
it( "returns false if interval_unit not 'month' but interval_count isn't 1", () => {
|
|
32
|
+
const introOffer: IntroOffer = {
|
|
33
|
+
...trialIntroOffer,
|
|
34
|
+
interval_count: 3,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
expect( isFirstMonthTrial( introOffer ) ).toBe( false );
|
|
38
|
+
} );
|
|
39
|
+
} );
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface IntroOffer {
|
|
2
|
+
product_id: number;
|
|
3
|
+
product_slug: string;
|
|
4
|
+
currency_code: string;
|
|
5
|
+
formatted_price: string;
|
|
6
|
+
original_price: number;
|
|
7
|
+
raw_price: number;
|
|
8
|
+
discount_percentage: number;
|
|
9
|
+
ineligible_reason: string[] | null;
|
|
10
|
+
interval_unit: string;
|
|
11
|
+
interval_count: number;
|
|
12
|
+
}
|