@automattic/jetpack-connection 1.2.13 → 1.3.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,14 @@
2
2
 
3
3
  ### This is a list detailing changes for the Jetpack RNA Connection Component releases.
4
4
 
5
+ ## [1.3.0] - 2025-07-23
6
+ ### Changed
7
+ - Connection: removed hardcoded custom errors and added support for dynamic errors. [#44281]
8
+
9
+ ## [1.2.14] - 2025-07-21
10
+ ### Changed
11
+ - Update package dependencies. [#44356]
12
+
5
13
  ## [1.2.13] - 2025-07-10
6
14
  ### Changed
7
15
  - Update package dependencies. [#44219]
@@ -1104,6 +1112,8 @@
1104
1112
  - `Main` and `ConnectUser` components added.
1105
1113
  - `JetpackRestApiClient` API client added.
1106
1114
 
1115
+ [1.3.0]: https://github.com/Automattic/jetpack-connection-js/compare/v1.2.14...v1.3.0
1116
+ [1.2.14]: https://github.com/Automattic/jetpack-connection-js/compare/v1.2.13...v1.2.14
1107
1117
  [1.2.13]: https://github.com/Automattic/jetpack-connection-js/compare/v1.2.12...v1.2.13
1108
1118
  [1.2.12]: https://github.com/Automattic/jetpack-connection-js/compare/v1.2.11...v1.2.12
1109
1119
  [1.2.11]: https://github.com/Automattic/jetpack-connection-js/compare/v1.2.10...v1.2.11
@@ -1,4 +1,4 @@
1
- @use '@wordpress/base-styles/breakpoints';
1
+ @use "@wordpress/base-styles/breakpoints";
2
2
 
3
3
  .jp-connection__connect-screen {
4
4
  --spacing-base: 8px;
@@ -29,7 +29,7 @@
29
29
  max-width: 100%;
30
30
 
31
31
  &:disabled {
32
- color: hsla( 0, 0%, 100%, 0.4 );
32
+ color: hsla(0, 0%, 100%, 0.4);
33
33
  }
34
34
 
35
35
  @media ( max-width: breakpoints.$break-medium ) {
@@ -48,8 +48,8 @@
48
48
  height: 1px;
49
49
  width: 1px;
50
50
  overflow: hidden;
51
- white-space: nowrap;
52
-
51
+ white-space: nowrap;
52
+
53
53
  &:empty {
54
54
  display: none;
55
55
  }
@@ -1,8 +1,8 @@
1
- @use '@wordpress/base-styles/mixins';
2
- @use '@automattic/jetpack-base-styles/style';
1
+ @use "@wordpress/base-styles/mixins";
2
+ @use "@automattic/jetpack-base-styles/style";
3
3
 
4
4
  .jp-connection__connect-screen-layout {
5
- background: var( --jp-white );
5
+ background: var(--jp-white);
6
6
  box-shadow: 0 0 40px rgba(0, 0, 0, 0.08);
7
7
  border-radius: 4px;
8
8
 
@@ -10,7 +10,8 @@
10
10
  display: none;
11
11
  }
12
12
 
13
- &__left, &__right {
13
+ &__left,
14
+ &__right {
14
15
  box-sizing: border-box;
15
16
  }
16
17
 
@@ -30,7 +31,7 @@
30
31
  font-weight: 700;
31
32
  font-size: 36px;
32
33
  line-height: 40px;
33
- color: var( --jp-black );
34
+ color: var(--jp-black);
34
35
  margin-top: 32px;
35
36
  margin-bottom: 0;
36
37
  }
@@ -40,12 +41,13 @@
40
41
  font-weight: 500;
41
42
  font-size: 24px;
42
43
  line-height: 32px;
43
- color: var( --jp-black );
44
+ color: var(--jp-black);
44
45
  margin-top: 32px;
45
46
  margin-bottom: 0;
46
47
  }
47
48
 
48
- p, li {
49
+ p,
50
+ li {
49
51
  font-style: normal;
50
52
  font-weight: 400;
51
53
  font-size: 16px;
@@ -58,19 +60,19 @@
58
60
  }
59
61
 
60
62
  a {
61
- color: var( --jp-black );
63
+ color: var(--jp-black);
62
64
  text-decoration: underline;
63
65
  height: auto;
64
66
  font: inherit;
65
67
  padding: 0;
66
68
 
67
69
  &:hover {
68
- color: var( --jp-black );
69
- text-decoration-thickness: var( --jp-underline-thickness );
70
+ color: var(--jp-black);
71
+ text-decoration-thickness: var(--jp-underline-thickness);
70
72
  }
71
73
 
72
74
  &:focus {
73
- color: var( --jp-black );
75
+ color: var(--jp-black);
74
76
  box-shadow: none !important;
75
77
  }
76
78
  }
@@ -84,7 +86,7 @@
84
86
  background-size: 24px;
85
87
  padding-left: 30px;
86
88
  margin-bottom: 9px;
87
- color: var( --jp-black );
89
+ color: var(--jp-black);
88
90
  }
89
91
  }
90
92
  }
@@ -99,7 +101,7 @@
99
101
 
100
102
  &__two-columns {
101
103
  display: flex;
102
- flex-wrap: wrap;
104
+ flex-wrap: wrap;
103
105
 
104
106
  .jp-connection__connect-screen-layout__left {
105
107
  flex-grow: 1;
@@ -113,7 +115,7 @@
113
115
  .jp-connection__connect-screen-layout__right {
114
116
  flex-grow: 1;
115
117
  flex-basis: 47%;
116
- background: #F9F9F6;
118
+ background: #f9f9f6;
117
119
  display: none;
118
120
 
119
121
  @include mixins.break-xlarge {
@@ -143,7 +145,7 @@
143
145
  padding: 4rem 6rem 4rem 4rem;
144
146
  }
145
147
  }
146
-
148
+
147
149
  .jp-connection__connect-screen-required-plan__pricing-card {
148
150
 
149
151
  /** Line up with the top of the product logo,
@@ -155,7 +157,7 @@
155
157
  top: calc(var(--spacing-base) * 9.25);
156
158
  right: calc(var(--spacing-base) * -45);
157
159
  }
158
-
160
+
159
161
  .jp-components__pricing-card {
160
162
  border-radius: var(--jp-border-radius-rna);
161
163
  width: 425px;
@@ -191,7 +193,7 @@
191
193
  height: 677px;
192
194
  z-index: 1;
193
195
 
194
- clip-path: polygon( 100% 0, 100% 100%, 0 0, 0 0 );
196
+ clip-path: polygon(100% 0, 100% 100%, 0 0, 0 0);
195
197
 
196
198
  &__blue,
197
199
  &__yellow,
@@ -207,7 +209,7 @@
207
209
  right: -100px;
208
210
  top: -275px;
209
211
  z-index: 3;
210
-
212
+
211
213
  background-color: var(--jp-blue-5);
212
214
  }
213
215
 
@@ -1,4 +1,4 @@
1
- @use '@wordpress/base-styles/mixins';
1
+ @use "@wordpress/base-styles/mixins";
2
2
 
3
3
  .jp-connection__connect-screen-layout__left {
4
4
 
@@ -57,8 +57,8 @@
57
57
  align-items: center;
58
58
 
59
59
  &:disabled {
60
- background: var(--jp-gray) !important;
61
- color: var(--jp-gray-20) !important;
60
+ background: var(--jp-gray) !important;
61
+ color: var(--jp-gray-20) !important;
62
62
  }
63
63
  }
64
64
 
@@ -1,4 +1,4 @@
1
- import { Spinner, useBreakpointMatch } from '@automattic/jetpack-components';
1
+ import { Spinner } from '@automattic/jetpack-components';
2
2
  import { Icon, Notice, Path, SVG } from '@wordpress/components';
3
3
  import { __, sprintf } from '@wordpress/i18n';
4
4
  import PropTypes from 'prop-types';
@@ -19,9 +19,7 @@ const ConnectionErrorNotice = props => {
19
19
  actions = [], // New prop for custom actions
20
20
  } = props;
21
21
 
22
- const [ isBiggerThanMedium ] = useBreakpointMatch( [ 'md' ], [ '>' ] );
23
- const wrapperClassName =
24
- styles.notice + ( isBiggerThanMedium ? ' ' + styles[ 'bigger-than-medium' ] : '' );
22
+ const wrapperClassName = styles.notice;
25
23
 
26
24
  const icon = (
27
25
  <Icon
@@ -82,31 +80,41 @@ const ConnectionErrorNotice = props => {
82
80
 
83
81
  if ( actions.length > 0 ) {
84
82
  // Use custom actions
85
- actionButtons = actions.map( ( action, index ) => (
86
- <a
87
- key={ index }
88
- onClick={ action.onClick }
89
- onKeyDown={ action.onClick }
90
- className={ `${ styles.button } ${ action.variant === 'primary' ? styles.primary : '' }` }
91
- href="#"
92
- >
93
- { action.isLoading
94
- ? action.loadingText || __( 'Loading…', 'jetpack-connection-js' )
95
- : action.label }
96
- </a>
97
- ) );
83
+ actionButtons = actions.map( ( action, index ) => {
84
+ let buttonClassName = styles.button;
85
+ if ( action.variant === 'primary' ) {
86
+ buttonClassName += ' ' + styles.primary;
87
+ } else if ( action.variant === 'secondary' ) {
88
+ buttonClassName += ' ' + styles.secondary;
89
+ }
90
+
91
+ return (
92
+ <button
93
+ key={ index }
94
+ type="button"
95
+ onClick={ action.onClick }
96
+ onKeyDown={ action.onClick }
97
+ className={ buttonClassName }
98
+ disabled={ action.isLoading }
99
+ >
100
+ { action.isLoading
101
+ ? action.loadingText || __( 'Loading…', 'jetpack-connection-js' )
102
+ : action.label }
103
+ </button>
104
+ );
105
+ } );
98
106
  } else if ( restoreConnectionCallback ) {
99
107
  // Use default restore connection action for backward compatibility
100
108
  actionButtons = [
101
- <a
109
+ <button
102
110
  key="restore"
111
+ type="button"
103
112
  onClick={ restoreConnectionCallback }
104
113
  onKeyDown={ restoreConnectionCallback }
105
114
  className={ styles.button }
106
- href="#"
107
115
  >
108
116
  { __( 'Restore Connection', 'jetpack-connection-js' ) }
109
- </a>,
117
+ </button>,
110
118
  ];
111
119
  }
112
120
 
@@ -5,18 +5,18 @@
5
5
  .notice {
6
6
  box-sizing: border-box;
7
7
  margin: 0;
8
- padding: calc( var( --spacing-base ) * 2 ) calc( var( --spacing-base ) * 3 ) calc( var( --spacing-base ) * 2 ) calc( var( --spacing-base ) * 3 ); // 16px | 24px | 16px | 24px
8
+ padding: calc(var(--spacing-base) * 2) calc(var(--spacing-base ) * 3) calc(var(--spacing-base) * 2) calc(var(--spacing-base) * 3); // 16px | 24px | 16px | 24px
9
9
  font-size: 16px;
10
10
  line-height: 22px;
11
- color: var( --jp-gray-80 );
12
- border: 1px solid var( --jp-gray );
13
- border-left: 6px solid var( --jp-red-50 );
11
+ color: var(--jp-gray-80);
12
+ border: 1px solid var(--jp-gray);
13
+ border-left: 6px solid var(--jp-red-50);
14
14
  border-radius: var( --jp-border-radius );
15
15
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.03), 0 1px 2px rgba(0, 0, 0, 0.06);
16
16
 
17
17
  // notice itself on error status
18
18
  &:global(.is-error) {
19
- background-color: var( --jp-white );
19
+ background-color: var(--jp-white);
20
20
  }
21
21
 
22
22
  // notice content
@@ -25,12 +25,12 @@
25
25
  margin: 0;
26
26
  padding: 12px 4px;
27
27
  flex-direction: column;
28
- align-items: flex-start;
28
+ align-items: center;
29
29
  }
30
30
 
31
31
  // action button
32
32
  :global(.is-link) {
33
- color: var( --jp-black );
33
+ color: var(--jp-black);
34
34
  font-size: 16px;
35
35
  font-weight: 600;
36
36
  }
@@ -40,8 +40,10 @@
40
40
  align-self: center;
41
41
  }
42
42
 
43
- .button, .button:visited, .button:active, .button:hover {
44
- color: var( --jp-white );
43
+ .button,
44
+ .button:visited,
45
+ .button:active,
46
+ .button:hover {
45
47
  font-weight: 600;
46
48
  font-size: 16px;
47
49
  line-height: 24px;
@@ -51,25 +53,74 @@
51
53
  justify-content: center;
52
54
  align-items: center;
53
55
  padding: 8px 24px;
54
- margin-left: calc( var( --spacing-base ) * 2 + 24px ); // 40px
56
+ border-radius: var(--jp-border-radius);
57
+ display: inline-block;
58
+ margin-left: calc(var(--spacing-base) * 2 + 24px); // 40px
55
59
  margin-top: 24px;
56
- background: #000;
57
- border-radius: var( --jp-border-radius );
60
+ }
61
+
62
+ .primary,
63
+ .primary:visited,
64
+ .primary:active,
65
+ .primary:hover {
66
+ color: var(--jp-white);
67
+ background: var(--jp-black);
68
+ border: 1px solid transparent;
69
+ }
58
70
 
59
- &.primary {
60
- background: var( --jp-green-50 );
71
+ .secondary,
72
+ .secondary:visited,
73
+ .secondary:active,
74
+ .secondary:hover {
75
+ background: transparent;
76
+ color: var(--jp-gray-80);
77
+ border: 1px solid var(--jp-gray-30);
78
+
79
+ &:hover {
80
+ color: var(--jp-gray-100);
81
+ border-color: var(--jp-gray-50);
61
82
  }
62
83
  }
63
84
 
64
85
  .actions {
65
86
  display: flex;
66
- gap: calc( var( --spacing-base ) * 2 ); // 16px
67
- flex-wrap: wrap;
87
+ gap: calc(var(--spacing-base) * 2); // 16px
88
+ flex-direction: column;
89
+ text-align: center;
90
+ margin-top: calc(var(--spacing-base) * 2); // 16px
91
+
92
+ .button,
93
+ .button:visited,
94
+ .button:active,
95
+ .button:hover {
96
+ width: 100%;
97
+ max-width: 300px;
98
+ margin: 0;
99
+ }
68
100
  }
69
101
 
70
- &.bigger-than-medium {
102
+ // Media query for screens 1100px and above
103
+ @media (min-width: 1100px) {
104
+
105
+ .actions {
106
+ margin-left: 0;
107
+ margin-top: 0;
108
+ flex-direction: row;
109
+ align-items: center;
110
+
111
+ .button,
112
+ .button:visited,
113
+ .button:active,
114
+ .button:hover {
115
+ width: auto;
116
+ max-width: none;
117
+ }
118
+ }
71
119
 
72
- .button, .button:visited, .button:active, .button:hover {
120
+ .button,
121
+ .button:visited,
122
+ .button:active,
123
+ .button:hover {
73
124
  margin-left: 0;
74
125
  margin-top: 0;
75
126
  white-space: nowrap;
@@ -87,7 +138,7 @@
87
138
  }
88
139
 
89
140
  .message {
90
- margin-right: var( --spacing-base ); // 8px
141
+ margin-right: var(--spacing-base); // 8px
91
142
  flex-grow: 1;
92
143
  display: flex;
93
144
 
@@ -95,10 +146,10 @@
95
146
  > svg {
96
147
  flex-shrink: 0;
97
148
  align-self: flex-start;
98
- margin-right: calc( var( --spacing-base ) * 2 ); // 16px
149
+ margin-right: calc(var(--spacing-base) * 2); // 16px
99
150
  }
100
151
 
101
152
  :global(.jp-components-spinner) {
102
- margin-right: calc( var( --spacing-base ) * 2 ); // 16px
153
+ margin-right: calc(var(--spacing-base) * 2); // 16px
103
154
  }
104
155
  }
@@ -1,14 +1,14 @@
1
- @use '@automattic/jetpack-base-styles/style';
1
+ @use "@automattic/jetpack-base-styles/style";
2
2
 
3
- // Used to show cards in the disconnection flow for active plugins and site benefits.
3
+ // Show cards in the disconnection flow for active plugins and site benefits.
4
4
  .jp-connection__disconnect-card {
5
- background-color: var( --jp-white );
5
+ background-color: var(--jp-white);
6
6
  width: 800px;
7
7
  max-width: 100%;
8
8
  padding: 1rem 2rem;
9
9
  border: none;
10
10
  border-radius: 3px;
11
- box-shadow: 0 0 15px var( --jp-gray-off );
11
+ box-shadow: 0 0 15px var(--jp-gray-off);
12
12
  margin-left: auto;
13
13
  margin-right: auto;
14
14
  margin-top: 0;
@@ -1,24 +1,24 @@
1
- @use '@automattic/jetpack-base-styles/style';
1
+ @use "@automattic/jetpack-base-styles/style";
2
2
 
3
3
  .jp-connection__disconnect-dialog {
4
4
 
5
5
  h1 {
6
6
  margin-top: 0;
7
7
  line-height: 1.2;
8
- font-size: var( --font-title-small );
8
+ font-size: var(--font-title-small);
9
9
  font-weight: 600;
10
10
  }
11
11
 
12
12
  h2 {
13
13
  margin: 0;
14
14
  line-height: 1.2;
15
- font-size: var( --font-title-small );
15
+ font-size: var(--font-title-small);
16
16
  font-weight: 400;
17
17
  }
18
18
 
19
19
  p {
20
20
  margin-top: 0;
21
- font-size: var( --font-body );
21
+ font-size: var(--font-body);
22
22
  }
23
23
 
24
24
  &__large-text,
@@ -28,36 +28,36 @@
28
28
 
29
29
  &__link,
30
30
  .jp-connection__disconnect-dialog__link {
31
- color: var( --jp-black );
31
+ color: var(--jp-black);
32
32
  text-decoration: underline;
33
33
  height: auto;
34
34
  font: inherit;
35
35
  padding: 0;
36
36
 
37
37
  &:hover {
38
- color: var( --jp-black );
39
- text-decoration-thickness: var( --jp-underline-thickness );
38
+ color: var(--jp-black);
39
+ text-decoration-thickness: var(--jp-underline-thickness);
40
40
  }
41
41
 
42
42
  &:focus {
43
- color: var( --jp-black );
43
+ color: var(--jp-black);
44
44
  box-shadow: none !important;
45
45
  }
46
46
 
47
47
  &--bold {
48
- font-weight: 700;
48
+ font-weight: 700;
49
49
  }
50
50
  }
51
51
 
52
- .components-button{
52
+ .components-button {
53
53
  height: 40px;
54
- font-size: var( --font-body-small );
54
+ font-size: var(--font-body-small);
55
55
  border-radius: 4px;
56
56
  }
57
57
 
58
- .components-modal{
58
+ .components-modal {
59
59
 
60
- &__content{
60
+ &__content {
61
61
  padding: 0;
62
62
  display: flex;
63
63
  flex-direction: column;
@@ -69,7 +69,7 @@
69
69
  }
70
70
  }
71
71
 
72
- &__header{
72
+ &__header {
73
73
  display: none;
74
74
  }
75
75
  }
@@ -79,44 +79,44 @@
79
79
  width: calc(100% - 48px); // Help with the margin on the row.
80
80
  }
81
81
 
82
- &__actions{
83
- background: var( --jp-white );
82
+ &__actions {
83
+ background: var(--jp-white);
84
84
  padding: 2rem 0;
85
85
  position: sticky;
86
86
  bottom: 0;
87
- border-top: 1px solid var( --jp-gray );
87
+ border-top: 1px solid var(--jp-gray);
88
88
 
89
89
  p {
90
90
  margin-bottom: 0;
91
91
  }
92
92
 
93
93
  &::before {
94
- content: '';
94
+ content: "";
95
95
  display: block;
96
96
  width: 100%;
97
97
  position: absolute;
98
98
  height: 80px;
99
- background: linear-gradient(to bottom, rgba( 0,0,0,0 ), var( --jp-white-off ));
100
- bottom: calc( 100% + 1px );
99
+ background: linear-gradient(to bottom, rgba(0, 0, 0, 0), var(--jp-white-off));
100
+ bottom: calc(100% + 1px);
101
101
  left: 0;
102
102
  }
103
103
  }
104
104
 
105
105
  &__btn-dismiss,
106
- &__btn-dismiss.components-button{ // override the components-button class
107
- background: var( --jp-black ) !important;
106
+ &__btn-dismiss.components-button { // override the components-button class
107
+ background: var(--jp-black) !important;
108
108
  margin-right: 10px;
109
109
  }
110
110
 
111
- &__btn-disconnect{
112
- background: var( --jp-red ) !important;
111
+ &__btn-disconnect {
112
+ background: var(--jp-red) !important;
113
113
  }
114
114
 
115
- &__btn-back-to-wp{
116
- background: var( --jp-black ) !important;
115
+ &__btn-back-to-wp {
116
+ background: var(--jp-black) !important;
117
117
  }
118
118
 
119
- &__button-wrap{
119
+ &__button-wrap {
120
120
  text-align: left;
121
121
 
122
122
  @media (min-width: 960px) {
@@ -124,8 +124,8 @@
124
124
  }
125
125
  }
126
126
 
127
- &__error{
128
- color: var( --jp-red );
127
+ &__error {
128
+ color: var(--jp-red);
129
129
  }
130
130
 
131
131
  &__survey {
@@ -145,7 +145,7 @@
145
145
  }
146
146
 
147
147
  .jp-connection__disconnect-dialog__content {
148
- background: var( --jp-white-off );
148
+ background: var(--jp-white-off);
149
149
  margin: 0;
150
150
  padding: 2rem 1rem;
151
151
  border-radius: 4px;
@@ -156,7 +156,8 @@
156
156
  justify-content: center;
157
157
  align-items: center;
158
158
 
159
- // When the screen height is shorter, hide the decorative cards to show the text and controls without scrolling.
159
+ // When the screen height is shorter, hide the decorative cards to
160
+ // show the text and controls without scrolling.
160
161
  @media (max-height: 900px) {
161
162
 
162
163
  .jp-components__decorative-card {
@@ -165,12 +166,12 @@
165
166
  }
166
167
  }
167
168
 
168
- @media (min-width: 600px){
169
+ @media (min-width: 600px) {
169
170
 
170
171
  .jp-connection__disconnect-dialog,
171
172
  .jp-connection__disconnect-dialog.components-modal__frame {
172
173
  width: 100%;
173
- max-width: calc( 100% - 32px );
174
+ max-width: calc(100% - 32px);
174
175
  }
175
176
 
176
177
  .jp-connection__disconnect-dialog {
@@ -186,7 +187,7 @@
186
187
  }
187
188
 
188
189
 
189
- @media (min-width: 960px){
190
+ @media (min-width: 960px) {
190
191
 
191
192
  .jp-connection__disconnect-dialog,
192
193
  .jp-connection__disconnect-dialog.components-modal__frame {
@@ -199,7 +200,7 @@
199
200
  .jp-connection__disconnect-dialog {
200
201
 
201
202
  h1 {
202
- font-size: var( --font-title-large );
203
+ font-size: var(--font-title-large);
203
204
  }
204
205
 
205
206
  &__large-text,
@@ -1,4 +1,4 @@
1
- @use '@automattic/jetpack-base-styles/style';
1
+ @use "@automattic/jetpack-base-styles/style";
2
2
 
3
3
  .jp-connect__disconnect-survey-card {
4
4
  border: 2px solid transparent;
@@ -10,24 +10,24 @@
10
10
  text-align: left;
11
11
  padding: 1rem;
12
12
  position: relative;
13
- box-shadow: 0 0 15px var( --jp-gray-off );
13
+ box-shadow: 0 0 15px var(--jp-gray-off);
14
14
 
15
15
  &--selected {
16
- border-color: var( --jp-black );
17
- background: var( --jp-gray-off );
16
+ border-color: var(--jp-black);
17
+ background: var(--jp-gray-off);
18
18
  }
19
19
 
20
20
  &::after {
21
- content: '';
21
+ content: "";
22
22
  display: block;
23
23
  width: 5px;
24
24
  height: 5px;
25
25
  position: absolute;
26
26
  top: 50%;
27
27
  right: 1.5rem;
28
- border-top: 2px solid var( --jp-black );
29
- border-right: 2px solid var( --jp-black );
30
- transform: translateY( -50% ) rotate( 45deg );
28
+ border-top: 2px solid var(--jp-black);
29
+ border-right: 2px solid var(--jp-black);
30
+ transform: translateY(-50%) rotate(45deg);
31
31
  }
32
32
 
33
33
  &:hover {
@@ -37,8 +37,8 @@
37
37
  &:hover,
38
38
  &:focus {
39
39
 
40
- &:not( .jp-disconnect-survey-card--selected ) {
41
- border-color: var( --jp-black-80 )
40
+ &:not(.jp-disconnect-survey-card--selected) {
41
+ border-color: var(--jp-black-80);
42
42
  }
43
43
  }
44
44
 
@@ -54,8 +54,8 @@ input.jp-connect__disconnect-survey-card__input {
54
54
  -webkit-appearance: none;
55
55
  border: none;
56
56
  background-color: transparent;
57
- color: var( --jp-black-80 );
57
+ color: var(--jp-black-80);
58
58
  flex-grow: 1;
59
59
  padding-right: 40px;
60
- max-width: calc( 100% - 40px );
60
+ max-width: calc(100% - 40px);
61
61
  }
@@ -8,7 +8,11 @@
8
8
 
9
9
  @keyframes fadeIn {
10
10
 
11
- 0% {opacity:0;}
11
+ 0% {
12
+ opacity: 0;
13
+ }
12
14
 
13
- 100% {opacity:1;}
15
+ 100% {
16
+ opacity: 1;
17
+ }
14
18
  }
@@ -1,4 +1,4 @@
1
- @use '@automattic/jetpack-base-styles/style';
1
+ @use "@automattic/jetpack-base-styles/style";
2
2
 
3
3
  .jp-connection__manage-dialog {
4
4
  --spacing-base: 8px;
@@ -6,8 +6,8 @@
6
6
  width: 1200px;
7
7
  border-radius: 3px;
8
8
 
9
- &__content{
10
- background: var( --jp-white-off );
9
+ &__content {
10
+ background: var(--jp-white-off);
11
11
  padding: 80px;
12
12
  text-align: center;
13
13
  display: flex;
@@ -17,7 +17,7 @@
17
17
 
18
18
  h1 {
19
19
  margin: 0;
20
- font-size: var( --font-title-large );
20
+ font-size: var(--font-title-large);
21
21
  font-weight: 700;
22
22
  line-height: 1.2;
23
23
  }
@@ -25,67 +25,67 @@
25
25
 
26
26
  // shorthand intentionally not used here for overriding the .reset class
27
27
  .jp-connection__manage-dialog__large-text {
28
- margin-top: calc( var( --spacing-base ) * 3 );
29
- margin-bottom: calc( var( --spacing-base ) * 4 );
28
+ margin-top: calc(var(--spacing-base) * 3);
29
+ margin-bottom: calc(var(--spacing-base) * 4);
30
30
  max-width: 60%;
31
31
  font-weight: 600;
32
32
  font-size: 1.25rem;
33
33
  }
34
34
 
35
- &__actions{
36
- background: var( --jp-white );
37
- margin:0 !important;
38
- border-top: 1px solid var( --jp-gray );
35
+ &__actions {
36
+ background: var(--jp-white);
37
+ margin: 0 !important;
38
+ border-top: 1px solid var(--jp-gray);
39
39
  max-width: 1200px !important;
40
- padding: calc( var( --spacing-base ) * 4 ) calc( var( --spacing-base ) * 5 );
41
- align-items:center;
40
+ padding: calc(var(--spacing-base) * 4) calc(var(--spacing-base) * 5);
41
+ align-items: center;
42
42
  position: sticky;
43
43
  bottom: 0;
44
44
  box-sizing: border-box;
45
45
  }
46
46
 
47
47
  &__link {
48
- color: var( --jp-black );
48
+ color: var(--jp-black);
49
49
 
50
50
  &:hover {
51
- color: var( --jp-black );
52
- text-decoration-thickness: var( --jp-underline-thickness );
51
+ color: var(--jp-black);
52
+ text-decoration-thickness: var(--jp-underline-thickness);
53
53
  }
54
54
 
55
55
  &:focus {
56
- color: var( --jp-black );
56
+ color: var(--jp-black);
57
57
  }
58
58
  }
59
59
 
60
- &__button-wrap button{
61
- float:right;
60
+ &__button-wrap button {
61
+ float: right;
62
62
  }
63
63
 
64
64
  &__action-card {
65
- background-color: var( --jp-white );
65
+ background-color: var(--jp-white);
66
66
  width: 750px;
67
67
  max-width: 100%;
68
68
  padding: 1rem 2rem;
69
69
  border-radius: 3px;
70
- box-shadow: 0 0 15px var( --jp-gray-off );
71
- margin: var( --spacing-base ) auto;
70
+ box-shadow: 0 0 15px var(--jp-gray-off);
71
+ margin: var(--spacing-base) auto;
72
72
  text-align: left;
73
73
  border: none;
74
74
  position: relative;
75
75
 
76
76
  &__card-headline {
77
- font-size: var( --font-body );
77
+ font-size: var(--font-body);
78
78
  font-weight: 600;
79
79
  text-decoration: none;
80
- line-height: calc( var(--spacing-base) * 3 );
80
+ line-height: calc(var(--spacing-base) * 3);
81
81
  }
82
82
 
83
83
  &__icon {
84
- float:right;
84
+ float: right;
85
85
  }
86
86
 
87
87
  &.disabled::before {
88
- content: '';
88
+ content: "";
89
89
  display: block;
90
90
  position: absolute;
91
91
  top: 0;
@@ -94,22 +94,23 @@
94
94
  height: 100%;
95
95
  opacity: 25%;
96
96
  border-radius: 3px;
97
- background: var( --jp-black );
97
+ background: var(--jp-black);
98
98
  }
99
99
 
100
- .transfer, .unlink {
101
- color:var( --jp-black );
102
- fill: var( --jp-black );
100
+ .transfer,
101
+ .unlink {
102
+ color: var(--jp-black);
103
+ fill: var(--jp-black);
103
104
  }
104
105
 
105
106
  .disconnect {
106
- color: var( --jp-red );
107
- fill: var( --jp-red );
107
+ color: var(--jp-red);
108
+ fill: var(--jp-red);
108
109
  }
109
110
 
110
111
  .check-users {
111
- color: var( --jp-black );
112
- fill: var( --jp-black );
112
+ color: var(--jp-black);
113
+ fill: var(--jp-black);
113
114
  }
114
115
 
115
116
  }
@@ -117,7 +118,7 @@
117
118
  .components-notice {
118
119
  width: 750px;
119
120
  max-width: 100%;
120
- margin: var( --spacing-base ) auto;
121
+ margin: var(--spacing-base) auto;
121
122
  text-align: left;
122
123
  }
123
124
 
@@ -129,7 +130,7 @@
129
130
 
130
131
  &__content {
131
132
  margin: 0;
132
- padding:0;
133
+ padding: 0;
133
134
  }
134
135
  }
135
136
  }
@@ -1,16 +1,16 @@
1
1
  .jp-connection__disconnect-dialog {
2
- // Add !important to override WordPress styles
3
- .components-button.jp-connection__disconnect-dialog__btn-dismiss {
4
- background: var( --jp-black ) !important;
5
- }
2
+ // Add !important to override WordPress styles
3
+ .components-button.jp-connection__disconnect-dialog__btn-dismiss {
4
+ background: var(--jp-black) !important;
5
+ }
6
6
 
7
- .jp-connection__disconnect-dialog__content {
8
- --spacing-base: 8px;
9
- }
7
+ .jp-connection__disconnect-dialog__content {
8
+ --spacing-base: 8px;
9
+ }
10
10
 
11
- .components-modal__content > div:not(.components-modal__header) {
12
- display: flex;
13
- flex-direction: column;
14
- height: 100%;
15
- }
11
+ .components-modal__content > div:not(.components-modal__header) {
12
+ display: flex;
13
+ flex-direction: column;
14
+ height: 100%;
15
+ }
16
16
  }
@@ -3,34 +3,6 @@ import ConnectionErrorNotice from '../../components/connection-error-notice';
3
3
  import useConnection from '../../components/use-connection';
4
4
  import useRestoreConnection from '../../hooks/use-restore-connection/index.jsx';
5
5
 
6
- /**
7
- * Helper function to generate user creation URL with email prepopulation
8
- *
9
- * @param {object} connectionError - The connection error object
10
- * @param {string} baseUrl - Base admin URL (defaults to '/wp-admin/')
11
- * @return {string} The complete URL for user creation with email parameters
12
- */
13
- export function getProtectedOwnerCreateAccountUrl( connectionError, baseUrl = '/wp-admin/' ) {
14
- let redirectUrl = baseUrl + 'user-new.php';
15
-
16
- // Add protected owner email if available for prepopulation
17
- if ( connectionError?.error_data?.wpcom_user_email ) {
18
- const params = new URLSearchParams( {
19
- jetpack_protected_owner_email: connectionError.error_data.wpcom_user_email,
20
- jetpack_create_missing_account: '1',
21
- } );
22
- redirectUrl += '?' + params.toString();
23
- } else if ( connectionError?.error_data?.email ) {
24
- const params = new URLSearchParams( {
25
- jetpack_protected_owner_email: connectionError.error_data.email,
26
- jetpack_create_missing_account: '1',
27
- } );
28
- redirectUrl += '?' + params.toString();
29
- }
30
-
31
- return redirectUrl;
32
- }
33
-
34
6
  /**
35
7
  * Connection error notice hook.
36
8
  * Returns connection error data and conditional flag on whether
@@ -48,7 +20,7 @@ export default function useConnectionErrorNotice() {
48
20
 
49
21
  const connectionErrorMessage = firstError && firstError.error_message;
50
22
 
51
- // Return all connection errors, including protected owner errors
23
+ // Return all connection errors
52
24
  const hasConnectionError = Boolean( connectionErrorMessage );
53
25
 
54
26
  return {
@@ -60,9 +32,9 @@ export default function useConnectionErrorNotice() {
60
32
  }
61
33
 
62
34
  export const ConnectionError = ( {
63
- onCreateMissingAccount = null, // Custom handler for protected owner errors
35
+ actionHandlers = {}, // Handlers for specific actions like { create_missing_account: () => {}, custom_action: () => {} }
64
36
  trackingCallback = null, // Custom tracking function
65
- customActions = null, // Function that returns custom actions based on error
37
+ customActions = null, // Function that returns custom actions based on error (takes precedence)
66
38
  } = {} ) => {
67
39
  const { hasConnectionError, connectionErrorMessage, connectionError } =
68
40
  useConnectionErrorNotice();
@@ -73,48 +45,140 @@ export const ConnectionError = ( {
73
45
  return null;
74
46
  }
75
47
 
76
- // Determine error type
77
- const isProtectedOwnerError = connectionError && connectionError.error_type === 'protected_owner';
78
-
79
- // Build actions array based on error type
48
+ // Build actions array based on error data
80
49
  let actions = [];
81
50
 
82
51
  if ( customActions ) {
83
52
  // Use provided custom actions function
84
- actions = customActions( connectionError, { restoreConnection, isRestoringConnection } );
85
- } else if ( isProtectedOwnerError && onCreateMissingAccount ) {
86
- // Handle protected owner error with custom handler
87
- actions = [
88
- {
89
- label: __( 'Create missing account', 'jetpack-connection-js' ),
90
- onClick: () => {
91
- if ( trackingCallback ) {
92
- trackingCallback( 'jetpack_connection_protected_owner_create_account_attempt', {} );
93
- }
94
- onCreateMissingAccount();
53
+ try {
54
+ actions = customActions( connectionError, { restoreConnection, isRestoringConnection } );
55
+ } catch {
56
+ // Silently fall back to default behavior if customActions fails
57
+ actions = [];
58
+ }
59
+ } else {
60
+ // Get action info from error data
61
+ const errorData = connectionError?.error_data || {};
62
+ const suggestedAction = errorData.action;
63
+ const actionHandler = actionHandlers[ suggestedAction ];
64
+
65
+ if ( suggestedAction && actionHandler ) {
66
+ // Use action data from the error
67
+ const actionLabel = errorData.action_label || __( 'Take Action', 'jetpack-connection-js' );
68
+ const actionVariant = errorData.action_variant || 'primary';
69
+ const trackingEvent = errorData.tracking_event;
70
+
71
+ actions = [
72
+ {
73
+ label: actionLabel,
74
+ onClick: () => {
75
+ try {
76
+ if ( trackingCallback && trackingEvent ) {
77
+ trackingCallback( trackingEvent, {} );
78
+ }
79
+ actionHandler( connectionError );
80
+ } catch {
81
+ // Silently fail if action handler throws
82
+ }
83
+ },
84
+ variant: actionVariant,
85
+ },
86
+ ];
87
+ } else if ( errorData.action_url && errorData.action_label ) {
88
+ // Generic link action - requires both URL and label for clarity
89
+ const actionLabel = errorData.action_label;
90
+ const actionVariant = errorData.action_variant || 'primary';
91
+ const trackingEvent = errorData.tracking_event;
92
+
93
+ actions = [
94
+ {
95
+ label: actionLabel,
96
+ onClick: () => {
97
+ try {
98
+ if ( trackingCallback && trackingEvent ) {
99
+ trackingCallback( trackingEvent, {} );
100
+ }
101
+ window.location.href = errorData.action_url;
102
+ } catch {
103
+ // Silently fail if navigation throws
104
+ }
105
+ },
106
+ variant: actionVariant,
95
107
  },
96
- variant: 'primary',
97
- },
98
- ];
99
- } else if ( ! isProtectedOwnerError ) {
100
- // Standard connection error - use restore connection
101
- actions = [
102
- {
103
- label: __( 'Restore Connection', 'jetpack-connection-js' ),
104
- onClick: () => {
105
- if ( trackingCallback ) {
106
- trackingCallback( 'jetpack_connection_error_notice_reconnect_cta_click', {} );
107
- }
108
- restoreConnection();
108
+ ];
109
+ } else {
110
+ // Default action - restore connection
111
+ actions = [
112
+ {
113
+ label: __( 'Restore Connection', 'jetpack-connection-js' ),
114
+ onClick: () => {
115
+ try {
116
+ if ( trackingCallback ) {
117
+ trackingCallback( 'jetpack_connection_error_notice_reconnect_cta_click', {} );
118
+ }
119
+ restoreConnection();
120
+ } catch {
121
+ // Silently fail if restore connection throws
122
+ }
123
+ },
124
+ isLoading: isRestoringConnection,
125
+ loadingText: __( 'Reconnecting Jetpack…', 'jetpack-connection-js' ),
109
126
  },
110
- isLoading: isRestoringConnection,
111
- loadingText: __( 'Reconnecting Jetpack…', 'jetpack-connection-js' ),
112
- },
113
- ];
127
+ ];
128
+ }
129
+
130
+ // Add secondary action if available (only for custom errors, not default restore)
131
+ if ( actions.length > 0 && ( suggestedAction || errorData.action_url ) ) {
132
+ const secondaryAction = errorData.secondary_action;
133
+ const secondaryActionHandler = actionHandlers[ secondaryAction ];
134
+ const secondaryActionUrl = errorData.secondary_action_url;
135
+ const secondaryActionLabel = errorData.secondary_action_label;
136
+
137
+ // Secondary action with handler
138
+ if ( secondaryAction && secondaryActionHandler && secondaryActionLabel ) {
139
+ const secondaryActionVariant = errorData.secondary_action_variant || 'secondary';
140
+ const secondaryTrackingEvent = errorData.secondary_tracking_event;
141
+
142
+ actions.push( {
143
+ label: secondaryActionLabel,
144
+ onClick: () => {
145
+ try {
146
+ if ( trackingCallback && secondaryTrackingEvent ) {
147
+ trackingCallback( secondaryTrackingEvent, {} );
148
+ }
149
+ secondaryActionHandler( connectionError );
150
+ } catch {
151
+ // Silently fail if secondary action handler throws
152
+ }
153
+ },
154
+ variant: secondaryActionVariant,
155
+ } );
156
+ }
157
+ // Secondary action with URL (requires both URL and label)
158
+ else if ( secondaryActionUrl && secondaryActionLabel ) {
159
+ const secondaryActionVariant = errorData.secondary_action_variant || 'secondary';
160
+ const secondaryTrackingEvent = errorData.secondary_tracking_event;
161
+
162
+ actions.push( {
163
+ label: secondaryActionLabel,
164
+ onClick: () => {
165
+ try {
166
+ if ( trackingCallback && secondaryTrackingEvent ) {
167
+ trackingCallback( secondaryTrackingEvent, {} );
168
+ }
169
+ window.location.href = secondaryActionUrl;
170
+ } catch {
171
+ // Silently fail if secondary action navigation throws
172
+ }
173
+ },
174
+ variant: secondaryActionVariant,
175
+ } );
176
+ }
177
+ }
114
178
  }
115
179
 
116
- // For protected owner errors without custom handler, don't show the component
117
- if ( isProtectedOwnerError && ! onCreateMissingAccount && ! customActions ) {
180
+ // If no actions are available and no custom handler provided, don't render
181
+ if ( actions.length === 0 && ! customActions ) {
118
182
  return null;
119
183
  }
120
184
 
package/index.jsx CHANGED
@@ -49,7 +49,4 @@ export { STORE_ID as CONNECTION_STORE_ID } from './state/store';
49
49
  */
50
50
  export { default as useProductCheckoutWorkflow } from './hooks/use-product-checkout-workflow';
51
51
  export { default as useRestoreConnection } from './hooks/use-restore-connection';
52
- export {
53
- default as useConnectionErrorNotice,
54
- getProtectedOwnerCreateAccountUrl,
55
- } from './hooks/use-connection-error-notice';
52
+ export { default as useConnectionErrorNotice } from './hooks/use-connection-error-notice';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/jetpack-connection",
3
- "version": "1.2.13",
3
+ "version": "1.3.0",
4
4
  "description": "Jetpack Connection Component",
5
5
  "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/connection/#readme",
6
6
  "bugs": {
@@ -16,7 +16,7 @@
16
16
  "dependencies": {
17
17
  "@automattic/jetpack-analytics": "^1.0.3",
18
18
  "@automattic/jetpack-api": "^1.0.5",
19
- "@automattic/jetpack-components": "^1.1.13",
19
+ "@automattic/jetpack-components": "^1.1.15",
20
20
  "@automattic/jetpack-config": "^1.0.3",
21
21
  "@automattic/jetpack-script-data": "^0.5.0",
22
22
  "@wordpress/base-styles": "6.2.0",
@@ -31,8 +31,8 @@
31
31
  "prop-types": "^15.7.2"
32
32
  },
33
33
  "devDependencies": {
34
- "@automattic/jetpack-base-styles": "^1.0.4",
35
- "@babel/core": "7.27.7",
34
+ "@automattic/jetpack-base-styles": "^1.0.6",
35
+ "@babel/core": "7.28.0",
36
36
  "@babel/preset-react": "7.27.1",
37
37
  "@testing-library/dom": "10.4.0",
38
38
  "@testing-library/react": "16.3.0",