@automattic/jetpack-shared-extension-utils 0.1.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.1] - 2022-03-29
9
+ ### Added
10
+ - Add missing JS peer dependency.
11
+
12
+ ## [0.3.0] - 2022-03-23
13
+ ### Added
14
+ - Moved plan-utils.js file from plugin/jetpack to shared-extension-utils. Updated import references for the same
15
+
16
+ ### Changed
17
+ - Updated package dependencies
18
+
19
+ ## [0.2.0] - 2022-03-15
20
+ ### Added
21
+ - Moved with-has-warning-is-interactive-class-names folder from jetpack plugin shared extensions
22
+
8
23
  ## [0.1.1] - 2022-03-02
9
24
  ### Changed
10
25
  - Updated package dependencies
@@ -21,4 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
21
36
  ### Changed
22
37
  - Core: prepare utility for release
23
38
 
39
+ [0.3.1]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.3.0...0.3.1
40
+ [0.3.0]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.2.0...0.3.0
41
+ [0.2.0]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.1.1...0.2.0
24
42
  [0.1.1]: https://github.com/Automattic/jetpack-shared-extension-utils/compare/0.1.0...0.1.1
package/index.js CHANGED
@@ -3,3 +3,13 @@ export { default as getSiteFragment } from './src/get-site-fragment';
3
3
  export { isSimpleSite, isAtomicSite, isPrivateSite } from './src/site-type-utils';
4
4
  export { default as getJetpackExtensionAvailability } from './src/get-jetpack-extension-availability';
5
5
  export { default as registerJetpackPlugin } from './src/register-jetpack-plugin';
6
+ export { default as withHasWarningIsInteractiveClassNames } from './src/with-has-warning-is-interactive-class-names';
7
+ export {
8
+ getUpgradeUrl,
9
+ isUpgradable,
10
+ requiresPaidPlan,
11
+ getRequiredPlan,
12
+ isUpgradeNudgeEnabled,
13
+ isStillUsableWithFreePlan,
14
+ getUsableBlockProps,
15
+ } from './src/plan-utils';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/jetpack-shared-extension-utils",
3
- "version": "0.1.1",
3
+ "version": "0.3.1",
4
4
  "description": "Utility functions used by the block editor extensions",
5
5
  "homepage": "https://jetpack.com",
6
6
  "bugs": {
@@ -16,11 +16,17 @@
16
16
  "test": "NODE_ENV=test NODE_PATH=tests:. js-test-runner --jsdom --initfile=test-main.jsx 'glob:./!(node_modules)/**/test/*.@(jsx|js)'"
17
17
  },
18
18
  "dependencies": {
19
- "@wordpress/plugins": "4.1.3"
19
+ "@wordpress/compose": "5.1.2",
20
+ "@wordpress/i18n": "4.3.0",
21
+ "@wordpress/plugins": "4.1.1",
22
+ "@wordpress/url": "3.4.0",
23
+ "lodash": "4.17.21"
20
24
  },
21
25
  "devDependencies": {
22
- "jetpack-js-test-runner": "workspace:*",
23
- "nyc": "15.1.0"
26
+ "@babel/core": "7.17.8",
27
+ "@babel/preset-react": "7.16.7",
28
+ "nyc": "15.1.0",
29
+ "react": "17.0.2"
24
30
  },
25
31
  "exports": {
26
32
  ".": "./index.js"
@@ -0,0 +1,187 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { compact, get, startsWith, map, filter, head } from 'lodash';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { addQueryArgs } from '@wordpress/url';
10
+ import { __ } from '@wordpress/i18n';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import { isAtomicSite, isSimpleSite } from './site-type-utils';
16
+ import getJetpackData from './get-jetpack-data';
17
+ import getJetpackExtensionAvailability from './get-jetpack-extension-availability';
18
+ import getSiteFragment from './get-site-fragment';
19
+
20
+ /**
21
+ * Return the checkout URL to upgrade the site plan,
22
+ * depending on the plan, postId, and postType site values.
23
+ *
24
+ * @param {object} siteParams - Site params used to build the URL.
25
+ * @param {string} siteParams.planSlug - Plan slug.
26
+ * @param {object} siteParams.plan - An object with details about the plan.
27
+ * @param {number} siteParams.postId - Post id.
28
+ * @param {string} siteParams.postType - Post type.
29
+ * @returns {string} Upgrade URL.
30
+ */
31
+ export function getUpgradeUrl( { planSlug, plan, postId, postType } ) {
32
+ // WP.com plan objects have a dedicated `path_slug` field, Jetpack plan objects don't.
33
+ const planPathSlug = startsWith( planSlug, 'jetpack_' ) ? planSlug : get( plan, [ 'path_slug' ] );
34
+
35
+ // The full site editor has no set post type.
36
+ const redirect_to = ( undefined === postType
37
+ ? () => {
38
+ const queryParams = new URLSearchParams( window.location.search );
39
+
40
+ return addQueryArgs(
41
+ window.location.protocol +
42
+ `//${ getSiteFragment().replace( '::', '/' ) }/wp-admin/admin.php`,
43
+ {
44
+ page: 'gutenberg-edit-site',
45
+ postId: queryParams.get( 'postId' ),
46
+ postType: queryParams.get( 'postType' ),
47
+ plan_upgraded: 1,
48
+ }
49
+ );
50
+ }
51
+ : () => {
52
+ // The editor for CPTs has an `edit/` route fragment prefixed.
53
+ const postTypeEditorRoutePrefix = [ 'page', 'post' ].includes( postType ) ? '' : 'edit';
54
+
55
+ // Post-checkout: redirect back here.
56
+ return isSimpleSite()
57
+ ? addQueryArgs(
58
+ '/' +
59
+ compact( [ postTypeEditorRoutePrefix, postType, getSiteFragment(), postId ] ).join(
60
+ '/'
61
+ ),
62
+ {
63
+ plan_upgraded: 1,
64
+ }
65
+ )
66
+ : addQueryArgs(
67
+ window.location.protocol +
68
+ `//${ getSiteFragment().replace( '::', '/' ) }/wp-admin/post.php`,
69
+ {
70
+ action: 'edit',
71
+ post: postId,
72
+ plan_upgraded: 1,
73
+ }
74
+ );
75
+ } )();
76
+
77
+ // Redirect to calypso plans page for WoC sites.
78
+ if ( isAtomicSite() ) {
79
+ return addQueryArgs( `https://wordpress.com/plans/${ getSiteFragment() }`, {
80
+ redirect_to,
81
+ customerType: 'business',
82
+ } );
83
+ }
84
+
85
+ return (
86
+ planPathSlug &&
87
+ addQueryArgs( `https://wordpress.com/checkout/${ getSiteFragment() }/${ planPathSlug }`, {
88
+ redirect_to,
89
+ } )
90
+ );
91
+ }
92
+
93
+ /**
94
+ * Check if the block is upgradable, based on whether
95
+ * the block requires a paid plan.
96
+ *
97
+ * @param {string} name - Block name.
98
+ * @returns {boolean} True if the block is upgradable, false otherwise.
99
+ */
100
+ export function isUpgradable( name ) {
101
+ if ( ! name ) {
102
+ return false;
103
+ }
104
+
105
+ const blockName = /^jetpack\//.test( name ) ? name.substr( 8, name.length ) : name;
106
+ const { available, unavailableReason } = getJetpackExtensionAvailability( blockName );
107
+
108
+ return ! available && 'missing_plan' === unavailableReason;
109
+ }
110
+
111
+ /**
112
+ * Checks whether the block requires a paid plan or not.
113
+ *
114
+ * @param {string} unavailableReason - The reason why block is unavailable
115
+ * @param {object} details - The block details
116
+ * @returns {string|boolean} Either false if the block doesn't require a paid plan, or the actual plan name it requires.
117
+ */
118
+ export function requiresPaidPlan( unavailableReason, details ) {
119
+ if ( unavailableReason === 'missing_plan' ) {
120
+ return details.required_plan;
121
+ }
122
+ return false;
123
+ }
124
+
125
+ /**
126
+ * Returns the required plan slug for a passed block name.
127
+ *
128
+ * @param {string} name - Block name.
129
+ * @returns {string|boolean} Plan name if the block is upgradable, false otherwise.
130
+ */
131
+ export function getRequiredPlan( name ) {
132
+ if ( ! name ) {
133
+ return false;
134
+ }
135
+
136
+ const blockName = /^jetpack\//.test( name ) ? name.substr( 8, name.length ) : name;
137
+ const { details, unavailableReason } = getJetpackExtensionAvailability( blockName );
138
+
139
+ return requiresPaidPlan( unavailableReason, details );
140
+ }
141
+
142
+ /*
143
+ * Usable blocks list with a free plan.
144
+ * This array contains blocks that can be usable
145
+ * even with a free plan, as well as properties
146
+ * used to handle specific behaviour.
147
+ */
148
+ const usableBlockWithFreePlan = [
149
+ {
150
+ name: 'core/cover',
151
+ mediaPlaceholder: true,
152
+ mediaReplaceFlow: true,
153
+ fileType: 'video',
154
+ description: __( 'Upgrade your plan to use video covers', 'jetpack' ),
155
+ },
156
+ {
157
+ name: 'core/audio',
158
+ mediaPlaceholder: true,
159
+ mediaReplaceFlow: true,
160
+ fileType: 'audio',
161
+ description: __( 'Upgrade your plan to upload audio', 'jetpack' ),
162
+ },
163
+ ];
164
+
165
+ /**
166
+ * Return whether upgrade nudges are enabled or not.
167
+ *
168
+ * @returns {boolean} True if the Upgrade Nudge is enable. Otherwise, False.
169
+ */
170
+ export function isUpgradeNudgeEnabled() {
171
+ return get( getJetpackData(), 'jetpack.enable_upgrade_nudge', false );
172
+ }
173
+
174
+ /*
175
+ * Some blocks are still usable with a free plan.
176
+ * We can handle their dual behavior defining specifically
177
+ * when to show the upgrade banner
178
+ * through or the Paid Block context.
179
+ *
180
+ * @param {string} name - Block name to check.
181
+ * @returns {boolean} True is the block is usable with a Free plan. Otherwise, False.
182
+ */
183
+ export const isStillUsableWithFreePlan = name =>
184
+ map( usableBlockWithFreePlan, 'name' ).includes( name );
185
+
186
+ export const getUsableBlockProps = blockName =>
187
+ head( filter( usableBlockWithFreePlan, ( { name } ) => name === blockName ) );
@@ -0,0 +1,23 @@
1
+ /* eslint-disable react/react-in-jsx-scope */
2
+
3
+ /**
4
+ * External dependencies
5
+ */
6
+ import { createHigherOrderComponent } from '@wordpress/compose';
7
+
8
+ import './style.scss';
9
+
10
+ // Injecting the `has-warning` class into the block wrapper component gives us
11
+ // the right kind of borders around the block, both visually and conceptually.
12
+ // However, it also adds styling to prevent user interaction with that block.
13
+ // We thus add a new `is-interactive` class to be able to override that behavior.
14
+ export default name =>
15
+ createHigherOrderComponent(
16
+ BlockListBlock => props => (
17
+ <BlockListBlock
18
+ { ...props }
19
+ className={ props.name === name ? 'has-warning is-interactive' : props.className }
20
+ />
21
+ ),
22
+ 'withHasWarningIsInteractiveClassNames'
23
+ );
@@ -0,0 +1,13 @@
1
+ .block-editor-block-list__layout
2
+ // Override core styles inherited from `.has-warning`:
3
+ // we do want blocks with upgrade nudge warning to be interactive
4
+ .block-editor-block-list__block.has-warning.is-interactive {
5
+ > * {
6
+ pointer-events: auto;
7
+ user-select: auto;
8
+ }
9
+
10
+ &:after {
11
+ content: none;
12
+ }
13
+ }