@automattic/jetpack-components 0.59.0 → 0.60.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 CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ### This is a list detailing changes for the Jetpack RNA Components package releases.
4
4
 
5
+ ## [0.60.0] - 2024-11-14
6
+ ### Added
7
+ - Adds tooltips for each ThreatFixerButton state [#40111]
8
+
9
+ ### Fixed
10
+ - Fixes the loading placeholder that didn't disappear when the price loads. [#40157]
11
+
5
12
  ## [0.59.0] - 2024-11-11
6
13
  ### Added
7
14
  - Add ThreatsDataViews component [#39754]
@@ -1214,6 +1221,7 @@
1214
1221
  ### Changed
1215
1222
  - Update node version requirement to 14.16.1
1216
1223
 
1224
+ [0.60.0]: https://github.com/Automattic/jetpack-components/compare/0.59.0...0.60.0
1217
1225
  [0.59.0]: https://github.com/Automattic/jetpack-components/compare/0.58.1...0.59.0
1218
1226
  [0.58.1]: https://github.com/Automattic/jetpack-components/compare/0.58.0...0.58.1
1219
1227
  [0.58.0]: https://github.com/Automattic/jetpack-components/compare/0.57.0...0.58.0
@@ -55,7 +55,8 @@ const PricingCard: React.FC< PricingCardProps > = ( {
55
55
  ) }
56
56
  <h1 className="jp-components__pricing-card__title">{ props.title }</h1>
57
57
  <div className="jp-components__pricing-card__pricing">
58
- { props.priceBefore !== props.priceAfter && props.priceAfter > 0 ? (
58
+ { props.priceAfter === 0 && <LoadingPlaceholder width="100%" height={ 48 } /> }
59
+ { props.priceBefore !== props.priceAfter && props.priceAfter > 0 && (
59
60
  <div className="jp-components__pricing-card__price-before">
60
61
  <span className="jp-components__pricing-card__currency">
61
62
  { currencyObjectBefore.symbol }
@@ -71,8 +72,6 @@ const PricingCard: React.FC< PricingCardProps > = ( {
71
72
  ) }
72
73
  <div className="jp-components__pricing-card__price-strikethrough"></div>
73
74
  </div>
74
- ) : (
75
- <LoadingPlaceholder width="100%" height={ 48 } />
76
75
  ) }
77
76
  { props.priceAfter > 0 && (
78
77
  <>
@@ -1,8 +1,13 @@
1
- import { Button, Text, ActionPopover } from '@automattic/jetpack-components';
2
- import { CONTACT_SUPPORT_URL, type Threat, fixerStatusIsStale } from '@automattic/jetpack-scan';
3
- import { ExternalLink } from '@wordpress/components';
4
- import { createInterpolateElement, useCallback, useMemo, useState } from '@wordpress/element';
5
- import { __, sprintf } from '@wordpress/i18n';
1
+ import { Button } from '@automattic/jetpack-components';
2
+ import {
3
+ type Threat,
4
+ fixerIsInError,
5
+ fixerIsInProgress,
6
+ fixerStatusIsStale,
7
+ } from '@automattic/jetpack-scan';
8
+ import { Tooltip } from '@wordpress/components';
9
+ import { useCallback, useMemo } from '@wordpress/element';
10
+ import { __ } from '@wordpress/i18n';
6
11
  import styles from './styles.module.scss';
7
12
 
8
13
  /**
@@ -24,119 +29,133 @@ export default function ThreatFixerButton( {
24
29
  onClick: ( items: Threat[] ) => void;
25
30
  className?: string;
26
31
  } ): JSX.Element {
27
- const [ isPopoverVisible, setIsPopoverVisible ] = useState( false );
28
-
29
- const [ anchor, setAnchor ] = useState( null );
32
+ const fixerState = useMemo( () => {
33
+ const inProgress = threat.fixer && fixerIsInProgress( threat.fixer );
34
+ const error = threat.fixer && fixerIsInError( threat.fixer );
35
+ const stale = threat.fixer && fixerStatusIsStale( threat.fixer );
36
+ return { inProgress, error, stale };
37
+ }, [ threat.fixer ] );
30
38
 
31
- const children = useMemo( () => {
39
+ const tooltipText = useMemo( () => {
32
40
  if ( ! threat.fixable ) {
33
41
  return null;
34
42
  }
35
- if ( threat.fixer && 'error' in threat.fixer && threat.fixer.error ) {
36
- return __( 'Error', 'jetpack' );
43
+
44
+ if ( fixerState.error ) {
45
+ return __( 'An error occurred auto-fixing this threat.', 'jetpack' );
37
46
  }
38
- if ( threat.fixer && 'status' in threat.fixer && threat.fixer.status === 'in_progress' ) {
39
- return __( 'Fixing…', 'jetpack' );
47
+
48
+ if ( fixerState.stale ) {
49
+ return __( 'The auto-fixer is taking longer than expected.', 'jetpack' );
40
50
  }
41
- if ( threat.fixable.fixer === 'delete' ) {
42
- return __( 'Delete', 'jetpack' );
51
+
52
+ if ( fixerState.inProgress ) {
53
+ return __( 'An auto-fixer is in progress.', 'jetpack' );
43
54
  }
44
- if ( threat.fixable.fixer === 'update' ) {
45
- return __( 'Update', 'jetpack' );
55
+
56
+ switch ( threat.fixable.fixer ) {
57
+ case 'delete':
58
+ if ( threat.filename ) {
59
+ if ( threat.filename.endsWith( '/' ) ) {
60
+ return __( 'Deletes the directory that the infected file is in.', 'jetpack' );
61
+ }
62
+
63
+ if ( threat.signature === 'Core.File.Modification' ) {
64
+ return __( 'Deletes the unexpected file in a core WordPress directory.', 'jetpack' );
65
+ }
66
+
67
+ return __( 'Deletes the infected file.', 'jetpack' );
68
+ }
69
+
70
+ if ( threat.extension?.type === 'plugin' ) {
71
+ return __( 'Deletes the plugin directory to fix the threat.', 'jetpack' );
72
+ }
73
+
74
+ if ( threat.extension?.type === 'theme' ) {
75
+ return __( 'Deletes the theme directory to fix the threat.', 'jetpack' );
76
+ }
77
+ break;
78
+ case 'update':
79
+ return __( 'Upgrades the plugin or theme to a newer version.', 'jetpack' );
80
+ case 'replace':
81
+ case 'rollback':
82
+ if ( threat.filename ) {
83
+ return threat.signature === 'Core.File.Modification'
84
+ ? __(
85
+ 'Replaces the modified core WordPress file with the original clean version from the WordPress source code.',
86
+ 'jetpack'
87
+ )
88
+ : __(
89
+ 'Replaces the infected file with a previously backed up version that is clean.',
90
+ 'jetpack'
91
+ );
92
+ }
93
+
94
+ if ( threat.signature === 'php_hardening_WP_Config_NoSalts_001' ) {
95
+ return __(
96
+ 'Replaces the default salt keys in wp-config.php with unique ones.',
97
+ 'jetpack'
98
+ );
99
+ }
100
+ break;
101
+ default:
102
+ return __( 'An auto-fixer is available.', 'jetpack' );
46
103
  }
47
- return __( 'Fix', 'jetpack' );
48
- }, [ threat.fixable, threat.fixer ] );
104
+ }, [ threat, fixerState ] );
49
105
 
50
- const errorMessage = useMemo( () => {
51
- if ( threat.fixer && fixerStatusIsStale( threat.fixer ) ) {
52
- return __( 'The fixer is taking longer than expected.', 'jetpack' );
106
+ const buttonText = useMemo( () => {
107
+ if ( ! threat.fixable ) {
108
+ return null;
53
109
  }
54
110
 
55
- if ( threat.fixer && 'error' in threat.fixer && threat.fixer.error ) {
56
- return __( 'An error occurred auto-fixing this threat.', 'jetpack' );
111
+ if ( fixerState.error ) {
112
+ return __( 'Error', 'jetpack' );
57
113
  }
58
114
 
59
- return null;
60
- }, [ threat.fixer ] );
115
+ switch ( threat.fixable.fixer ) {
116
+ case 'delete':
117
+ return __( 'Delete', 'jetpack' );
118
+ case 'update':
119
+ return __( 'Update', 'jetpack' );
120
+ case 'replace':
121
+ case 'rollback':
122
+ return __( 'Replace', 'jetpack' );
123
+ default:
124
+ return __( 'Fix', 'jetpack' );
125
+ }
126
+ }, [ threat.fixable, fixerState.error ] );
61
127
 
62
128
  const handleClick = useCallback(
63
129
  ( event: React.MouseEvent ) => {
64
130
  event.stopPropagation();
65
- if ( errorMessage && ! isPopoverVisible ) {
66
- setIsPopoverVisible( true );
67
- return;
68
- }
69
131
  onClick( [ threat ] );
70
132
  },
71
- [ onClick, errorMessage, isPopoverVisible, threat ]
133
+ [ onClick, threat ]
72
134
  );
73
135
 
74
- const closePopover = useCallback( () => {
75
- setIsPopoverVisible( false );
76
- }, [] );
77
-
78
136
  if ( ! threat.fixable ) {
79
137
  return null;
80
138
  }
81
139
 
82
140
  return (
83
141
  <div>
84
- <Button
85
- size="small"
86
- weight="regular"
87
- variant="secondary"
88
- onClick={ handleClick }
89
- children={ children }
90
- className={ className }
91
- disabled={
92
- threat.fixer &&
93
- 'status' in threat.fixer &&
94
- threat.fixer.status === 'in_progress' &&
95
- ! errorMessage
96
- }
97
- isLoading={
98
- threat.fixer && 'status' in threat.fixer && threat.fixer.status === 'in_progress'
99
- }
100
- isDestructive={
101
- ( threat.fixable && threat.fixable.fixer === 'delete' ) ||
102
- ( threat.fixer && 'error' in threat.fixer && threat.fixer.error ) ||
103
- ( threat.fixer && fixerStatusIsStale( threat.fixer ) )
104
- }
105
- style={ { minWidth: '72px' } }
106
- ref={ setAnchor }
107
- />
108
- { isPopoverVisible && (
109
- <ActionPopover
110
- anchor={ anchor }
111
- buttonContent={ __( 'Retry Fix', 'jetpack' ) }
112
- hideCloseButton={ true }
113
- noArrow={ false }
142
+ <Tooltip className={ styles.tooltip } text={ tooltipText }>
143
+ <Button
144
+ size="small"
145
+ weight="regular"
146
+ variant="secondary"
114
147
  onClick={ handleClick }
115
- onClose={ closePopover }
116
- title={ __( 'Auto-fix error', 'jetpack' ) }
117
- >
118
- <Text>
119
- { createInterpolateElement(
120
- sprintf(
121
- /* translators: placeholder is an error message. */
122
- __(
123
- '%s Please try again or <supportLink>contact support</supportLink>.',
124
- 'jetpack'
125
- ),
126
- errorMessage
127
- ),
128
- {
129
- supportLink: (
130
- <ExternalLink
131
- href={ CONTACT_SUPPORT_URL }
132
- className={ styles[ 'support-link' ] }
133
- />
134
- ),
135
- }
136
- ) }
137
- </Text>
138
- </ActionPopover>
139
- ) }
148
+ children={ buttonText }
149
+ className={ className }
150
+ isLoading={ fixerState.inProgress }
151
+ isDestructive={
152
+ ( threat.fixable && threat.fixable.fixer === 'delete' ) ||
153
+ fixerState.error ||
154
+ fixerState.stale
155
+ }
156
+ style={ { minWidth: '72px' } }
157
+ />
158
+ </Tooltip>
140
159
  </div>
141
160
  );
142
161
  }
@@ -7,3 +7,10 @@
7
7
  box-shadow: none;
8
8
  }
9
9
  }
10
+
11
+ .tooltip {
12
+ margin-top: var( --spacing-base ) ;
13
+ max-width: 240px;
14
+ border-radius: 4px;
15
+ text-align: left;
16
+ }
@@ -385,7 +385,7 @@ export default function ThreatsDataViews( {
385
385
  ? [
386
386
  {
387
387
  id: THREAT_FIELD_AUTO_FIX,
388
- label: __( 'Auto-Fix', 'jetpack' ),
388
+ label: __( 'Auto-fix', 'jetpack' ),
389
389
  enableHiding: false,
390
390
  elements: [
391
391
  {
@@ -426,7 +426,7 @@ export default function ThreatsDataViews( {
426
426
  if ( dataFields.includes( 'fixable' ) ) {
427
427
  result.push( {
428
428
  id: THREAT_ACTION_FIX,
429
- label: __( 'Auto-Fix', 'jetpack' ),
429
+ label: __( 'Auto-fix', 'jetpack' ),
430
430
  isPrimary: true,
431
431
  supportsBulk: true,
432
432
  callback: onFixThreats,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/jetpack-components",
3
- "version": "0.59.0",
3
+ "version": "0.60.0",
4
4
  "description": "Jetpack Components Package",
5
5
  "author": "Automattic",
6
6
  "license": "GPL-2.0-or-later",
@@ -15,8 +15,8 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@automattic/format-currency": "1.0.1",
18
- "@automattic/jetpack-boost-score-api": "^0.1.45",
19
- "@automattic/jetpack-scan": "^0.1.0",
18
+ "@automattic/jetpack-boost-score-api": "^0.1.46",
19
+ "@automattic/jetpack-scan": "^0.2.0",
20
20
  "@babel/runtime": "^7",
21
21
  "@wordpress/browserslist-config": "6.11.0",
22
22
  "@wordpress/components": "28.11.0",
@@ -32,12 +32,12 @@
32
32
  "prop-types": "^15.7.2",
33
33
  "qrcode.react": "3.1.0",
34
34
  "react-slider": "2.0.5",
35
- "social-logos": "^3.1.11",
35
+ "social-logos": "^3.1.12",
36
36
  "uplot": "1.6.31",
37
37
  "uplot-react": "1.1.4"
38
38
  },
39
39
  "devDependencies": {
40
- "@automattic/jetpack-base-styles": "^0.6.35",
40
+ "@automattic/jetpack-base-styles": "^0.6.36",
41
41
  "@babel/core": "7.26.0",
42
42
  "@babel/preset-react": "7.25.9",
43
43
  "@jest/globals": "29.4.3",