@automattic/jetpack-connection 0.29.8

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.
Files changed (53) hide show
  1. package/.gitattributes +12 -0
  2. package/CHANGELOG.md +622 -0
  3. package/LICENSE.txt +357 -0
  4. package/README.md +284 -0
  5. package/SECURITY.md +38 -0
  6. package/components/connect-button/index.jsx +70 -0
  7. package/components/connect-screen/basic/index.jsx +104 -0
  8. package/components/connect-screen/basic/style.scss +46 -0
  9. package/components/connect-screen/basic/visual.jsx +109 -0
  10. package/components/connect-screen/layout/image-slider.jsx +37 -0
  11. package/components/connect-screen/layout/index.jsx +65 -0
  12. package/components/connect-screen/layout/style.scss +238 -0
  13. package/components/connect-screen/required-plan/index.jsx +127 -0
  14. package/components/connect-screen/required-plan/style.scss +109 -0
  15. package/components/connect-screen/required-plan/visual.jsx +151 -0
  16. package/components/connect-user/index.jsx +64 -0
  17. package/components/connected-plugins/index.jsx +67 -0
  18. package/components/connection-error-notice/index.jsx +110 -0
  19. package/components/connection-error-notice/styles.module.scss +97 -0
  20. package/components/disconnect-card/index.jsx +40 -0
  21. package/components/disconnect-card/style.scss +85 -0
  22. package/components/disconnect-dialog/images/disconnect-confirm.jpg +0 -0
  23. package/components/disconnect-dialog/images/disconnect-thanks.jpg +0 -0
  24. package/components/disconnect-dialog/index.jsx +409 -0
  25. package/components/disconnect-dialog/steps/step-disconnect-confirm.jsx +87 -0
  26. package/components/disconnect-dialog/steps/step-disconnect.jsx +206 -0
  27. package/components/disconnect-dialog/steps/step-survey.jsx +48 -0
  28. package/components/disconnect-dialog/steps/step-thank-you.jsx +54 -0
  29. package/components/disconnect-dialog/style.scss +218 -0
  30. package/components/disconnect-survey/_jp-connect_disconnect-survey-card.scss +60 -0
  31. package/components/disconnect-survey/index.jsx +181 -0
  32. package/components/disconnect-survey/survey-choice.jsx +43 -0
  33. package/components/in-place-connection/index.jsx +140 -0
  34. package/components/in-place-connection/style.scss +35 -0
  35. package/components/manage-connection-dialog/index.jsx +219 -0
  36. package/components/manage-connection-dialog/style.scss +106 -0
  37. package/components/use-connection/index.jsx +112 -0
  38. package/helpers/third-party-cookies-fallback.jsx +10 -0
  39. package/hooks/use-connection-error-notice/index.jsx +38 -0
  40. package/hooks/use-product-checkout-workflow/Readme.md +61 -0
  41. package/hooks/use-product-checkout-workflow/index.jsx +103 -0
  42. package/hooks/use-restore-connection/index.jsx +64 -0
  43. package/index.jsx +48 -0
  44. package/index.native.js +1 -0
  45. package/package.json +62 -0
  46. package/state/actions.jsx +166 -0
  47. package/state/controls.jsx +40 -0
  48. package/state/reducers.jsx +121 -0
  49. package/state/resolvers.jsx +32 -0
  50. package/state/selectors.jsx +35 -0
  51. package/state/store-holder.jsx +14 -0
  52. package/state/store-id.jsx +3 -0
  53. package/state/store.jsx +29 -0
@@ -0,0 +1,181 @@
1
+ import { Button } from '@wordpress/components';
2
+ import { __ } from '@wordpress/i18n';
3
+ import PropTypes from 'prop-types';
4
+ import React, { useCallback, useState } from 'react';
5
+ import SurveyChoice from './survey-choice';
6
+
7
+ /**
8
+ * Handles showing the disconnect survey.
9
+ *
10
+ * @param {object} props - The component props.
11
+ * @returns {React.Component} - DisconnectSurvey component.
12
+ */
13
+ const DisconnectSurvey = props => {
14
+ const { onSubmit, isSubmittingFeedback } = props;
15
+ const [ selectedAnswer, setSelectedAnswer ] = useState();
16
+ const [ customResponse, setCustomResponse ] = useState();
17
+
18
+ const options = [
19
+ {
20
+ id: 'troubleshooting',
21
+ answerText: __( "Troubleshooting - I'll be reconnecting afterwards.", 'jetpack' ),
22
+ },
23
+ {
24
+ id: 'not-working',
25
+ answerText: __( "I can't get it to work.", 'jetpack' ),
26
+ },
27
+ {
28
+ id: 'slowed-down-site',
29
+ answerText: __( 'It slowed down my site.', 'jetpack' ),
30
+ },
31
+ {
32
+ id: 'buggy',
33
+ answerText: __( "It's buggy.", 'jetpack' ),
34
+ },
35
+ {
36
+ id: 'what-does-it-do',
37
+ answerText: __( "I don't know what it does.", 'jetpack' ),
38
+ },
39
+ ];
40
+
41
+ const customOption = {
42
+ id: 'another-reason',
43
+ };
44
+
45
+ /**
46
+ * Handle Submission of the survey.
47
+ * Will send the survey response to the collection endpoint.
48
+ */
49
+ const handleSurveySubmit = useCallback( () => {
50
+ const answerText = selectedAnswer === customOption.id ? customResponse : '';
51
+ onSubmit( selectedAnswer, answerText );
52
+ }, [ onSubmit, customOption.id, customResponse, selectedAnswer ] );
53
+
54
+ /**
55
+ * Handle input into the custom response field.
56
+ *
57
+ * @param {object} e - onChange event for the custom input
58
+ */
59
+ const handleCustomResponse = useCallback(
60
+ e => {
61
+ const value = e.target.value;
62
+ e.stopPropagation();
63
+ setCustomResponse( value );
64
+ },
65
+ [ setCustomResponse ]
66
+ );
67
+
68
+ /**
69
+ * Checks to see if an option is the currently selected option, returns a css class name if it matches.
70
+ *
71
+ * @param {string} optionId - ID of the option to check for.
72
+ * @returns {string} - The "selected" class if this option is currently selected.
73
+ */
74
+ const selectedClass = optionId => {
75
+ if ( optionId === selectedAnswer ) {
76
+ return 'jp-connect__disconnect-survey-card--selected';
77
+ }
78
+
79
+ return '';
80
+ };
81
+
82
+ /**
83
+ * Event handler for keyboard events on the answer blocks.
84
+ *
85
+ * @param {string} answerId - The slug of the answer that has been selected.
86
+ * @param {object} e - Keydown event.
87
+ */
88
+ const handleAnswerKeyDown = useCallback(
89
+ ( answerId, e ) => {
90
+ switch ( e.key ) {
91
+ case 'Enter':
92
+ case 'Space':
93
+ case 'Spacebar':
94
+ case ' ':
95
+ setSelectedAnswer( answerId );
96
+ break;
97
+ }
98
+ },
99
+ [ setSelectedAnswer ]
100
+ );
101
+
102
+ /**
103
+ * Show all the survey options from the options array.
104
+ *
105
+ * @returns {React.ElementType []} - Mapped array of rendered survey options.
106
+ */
107
+ const renderOptions = () => {
108
+ return options.map( option => {
109
+ return (
110
+ <SurveyChoice
111
+ id={ option.id }
112
+ onClick={ setSelectedAnswer }
113
+ onKeyDown={ handleAnswerKeyDown }
114
+ className={ 'card jp-connect__disconnect-survey-card ' + selectedClass( option.id ) }
115
+ >
116
+ <p className="jp-connect__disconnect-survey-card__answer">{ option.answerText }</p>
117
+ </SurveyChoice>
118
+ );
119
+ } );
120
+ };
121
+
122
+ /**
123
+ * Show the custom input survey option.
124
+ * Contains an input field for a custom response.
125
+ *
126
+ * @returns {React.ElementType} - The custom survey option with an input field.
127
+ */
128
+ const renderCustomOption = () => {
129
+ return (
130
+ <SurveyChoice
131
+ id={ customOption.id }
132
+ key={ customOption.id }
133
+ onClick={ setSelectedAnswer }
134
+ onKeyDown={ handleAnswerKeyDown }
135
+ className={ 'card jp-connect__disconnect-survey-card ' + selectedClass( customOption.id ) }
136
+ >
137
+ <p className="jp-connect__disconnect-survey-card__answer">
138
+ { __( 'Other:', 'jetpack' ) }{ ' ' }
139
+ <input
140
+ placeholder={ __( 'share your experience', 'jetpack' ) }
141
+ className="jp-connect__disconnect-survey-card__input"
142
+ type="text"
143
+ value={ customResponse }
144
+ onChange={ handleCustomResponse }
145
+ maxLength={ 1000 } // Limit response length.
146
+ />
147
+ </p>
148
+ </SurveyChoice>
149
+ );
150
+ };
151
+
152
+ return (
153
+ <React.Fragment>
154
+ <div className="jp-connection__disconnect-dialog__survey">
155
+ { renderOptions() }
156
+ { renderCustomOption() }
157
+ </div>
158
+ <p>
159
+ <Button
160
+ disabled={ ! selectedAnswer || isSubmittingFeedback }
161
+ variant="primary"
162
+ onClick={ handleSurveySubmit }
163
+ className="jp-connection__disconnect-dialog__btn-back-to-wp"
164
+ >
165
+ { isSubmittingFeedback
166
+ ? __( 'Submitting…', 'jetpack' )
167
+ : __( 'Submit Feedback', 'jetpack', /* dummy arg to avoid bad minification */ 0 ) }
168
+ </Button>
169
+ </p>
170
+ </React.Fragment>
171
+ );
172
+ };
173
+
174
+ DisconnectSurvey.PropTypes = {
175
+ /** Callback handler function for when the survey response is submitted. */
176
+ onSubmit: PropTypes.func,
177
+ /** If the survey feedback is currently being saved/ submitted */
178
+ isSubmittingFeedback: PropTypes.bool,
179
+ };
180
+
181
+ export default DisconnectSurvey;
@@ -0,0 +1,43 @@
1
+ import React, { useCallback } from 'react';
2
+
3
+ import './_jp-connect_disconnect-survey-card.scss';
4
+
5
+ /**
6
+ * SurveyChoice - Present one choice in the survey.
7
+ *
8
+ * @param {string} props.id - The ID/slug string of the survey option
9
+ * @param {Function} props.onClick - Event handler for clicking on the survey option.
10
+ * @param {Function} props.onKeydown - Event handler for pressing a key on the survey option.
11
+ * @param {React.ElementType} props.children - Any passed elements as children to this component.
12
+ * @param {string} props.className - A class name to apply to the survey choice.
13
+ * @returns {React.Component} SurveyChoice - The SurveyChoice component.
14
+ */
15
+
16
+ const SurveyChoice = props => {
17
+ const { id, onClick, onKeyDown, children, className } = props;
18
+
19
+ const handleClick = useCallback( () => {
20
+ onClick( id );
21
+ }, [ id, onClick ] );
22
+
23
+ const handleKeyDown = useCallback(
24
+ e => {
25
+ onKeyDown( id, e );
26
+ },
27
+ [ id, onKeyDown ]
28
+ );
29
+
30
+ return (
31
+ <div
32
+ tabIndex="0"
33
+ role="button"
34
+ onClick={ handleClick }
35
+ onKeyDown={ handleKeyDown }
36
+ className={ 'card jp-connect__disconnect-survey-card ' + className }
37
+ >
38
+ { children }
39
+ </div>
40
+ );
41
+ };
42
+
43
+ export default SurveyChoice;
@@ -0,0 +1,140 @@
1
+ import { __ } from '@wordpress/i18n';
2
+ import PropTypes from 'prop-types';
3
+ import React, { useEffect, useRef } from 'react';
4
+
5
+ import './style.scss';
6
+
7
+ /**
8
+ * The in-place connection component.
9
+ *
10
+ * @param {object} props -- The properties.
11
+ * @param {string} props.title -- Element title.
12
+ * @param {boolean} props.isLoading -- Whether the element is still loading.
13
+ * @param {string|number} props.width -- Iframe width.
14
+ * @param {string|number} props.height -- Iframe height.
15
+ * @param {boolean} props.displayTOS -- Whether the site has connection owner connected.
16
+ * @param {boolean} props.scrollToIframe -- Whether we need to auto-scroll the window upon element rendering.
17
+ * @param {string} props.connectUrl -- The connection URL.
18
+ * @param {Function} props.onComplete -- The callback to be called upon complete of the connection process.
19
+ * @param {Function} props.onThirdPartyCookiesBlocked -- The callback to be called if third-party cookies are disabled.
20
+ * @param {string} props.location -- Component location identifier passed to WP.com.
21
+ * @returns {React.Component} The in-place connection component.
22
+ */
23
+ const InPlaceConnection = props => {
24
+ const {
25
+ title,
26
+ isLoading,
27
+ width,
28
+ displayTOS,
29
+ scrollToIframe,
30
+ connectUrl,
31
+ onComplete,
32
+ onThirdPartyCookiesBlocked,
33
+ location,
34
+ } = props;
35
+ let { height } = props;
36
+
37
+ const iframeWrapRef = useRef();
38
+ const iframeRef = useRef();
39
+
40
+ /**
41
+ * Handles messages received from inside the iframe.
42
+ *
43
+ * @param {object} e -- Event object.
44
+ */
45
+ const receiveData = e => {
46
+ if ( ! iframeRef.current || e.source !== iframeRef.current.contentWindow ) {
47
+ return;
48
+ }
49
+
50
+ switch ( e.data ) {
51
+ case 'close':
52
+ // Remove listener, our job here is done.
53
+ window.removeEventListener( 'message', receiveData );
54
+
55
+ if ( onComplete ) {
56
+ onComplete();
57
+ }
58
+ break;
59
+ case 'wpcom_nocookie':
60
+ // Third-party cookies blocked.
61
+ if ( onThirdPartyCookiesBlocked ) {
62
+ onThirdPartyCookiesBlocked();
63
+ }
64
+ break;
65
+ }
66
+ };
67
+
68
+ useEffect(
69
+ /**
70
+ * The component initialization.
71
+ */
72
+ () => {
73
+ // Scroll to the iframe container
74
+ if ( scrollToIframe ) {
75
+ window.scrollTo( 0, iframeWrapRef.current.offsetTop - 10 );
76
+ }
77
+
78
+ // Add an event listener to identify successful authorization via iframe.
79
+ window.addEventListener( 'message', receiveData );
80
+ }
81
+ );
82
+
83
+ // The URL looks like https://jetpack.wordpress.com/jetpack.authorize_iframe/1/. We need to include the trailing
84
+ // slash below so that we don't end up with something like /jetpack.authorize_iframe_iframe/
85
+ let src = connectUrl.replace( 'authorize/', 'authorize_iframe/' );
86
+
87
+ if ( ! src.includes( '?' ) ) {
88
+ src += '?';
89
+ }
90
+
91
+ if ( displayTOS ) {
92
+ src += '&display-tos';
93
+ height = ( parseInt( height ) + 50 ).toString();
94
+ }
95
+
96
+ src += '&iframe_height=' + parseInt( height );
97
+
98
+ if ( location ) {
99
+ src += '&iframe_source=' + location;
100
+ }
101
+
102
+ return (
103
+ <div className="dops-card fade-in jp-iframe-wrap" ref={ iframeWrapRef }>
104
+ <h1>{ title }</h1>
105
+ { isLoading ? (
106
+ <p>{ __( 'Loading…', 'jetpack' ) }</p>
107
+ ) : (
108
+ <iframe
109
+ title={ title }
110
+ width={ width }
111
+ height={ height }
112
+ src={ src }
113
+ ref={ iframeRef }
114
+ ></iframe>
115
+ ) }
116
+ </div>
117
+ );
118
+ };
119
+
120
+ InPlaceConnection.propTypes = {
121
+ title: PropTypes.string.isRequired,
122
+ isLoading: PropTypes.bool,
123
+ width: PropTypes.string,
124
+ height: PropTypes.string,
125
+ connectUrl: PropTypes.string.isRequired,
126
+ displayTOS: PropTypes.bool.isRequired,
127
+ scrollToIframe: PropTypes.bool,
128
+ onComplete: PropTypes.func,
129
+ onThirdPartyCookiesBlocked: PropTypes.func,
130
+ location: PropTypes.string,
131
+ };
132
+
133
+ InPlaceConnection.defaultProps = {
134
+ isLoading: false,
135
+ height: '300',
136
+ width: '100%',
137
+ scrollToIframe: false,
138
+ };
139
+
140
+ export default InPlaceConnection;
@@ -0,0 +1,35 @@
1
+ .jp-iframe-wrap {
2
+ text-align: center;
3
+ }
4
+
5
+ .fade-in {
6
+ animation: fadeIn ease 1.5s;
7
+ -webkit-animation: fadeIn ease 1.5s;
8
+ -moz-animation: fadeIn ease 1.5s;
9
+ -o-animation: fadeIn ease 1.5s;
10
+ -ms-animation: fadeIn ease 1.5s;
11
+ }
12
+ @keyframes fadeIn {
13
+ 0% {opacity:0;}
14
+ 100% {opacity:1;}
15
+ }
16
+
17
+ @-moz-keyframes fadeIn {
18
+ 0% {opacity:0;}
19
+ 100% {opacity:1;}
20
+ }
21
+
22
+ @-webkit-keyframes fadeIn {
23
+ 0% {opacity:0;}
24
+ 100% {opacity:1;}
25
+ }
26
+
27
+ @-o-keyframes fadeIn {
28
+ 0% {opacity:0;}
29
+ 100% {opacity:1;}
30
+ }
31
+
32
+ @-ms-keyframes fadeIn {
33
+ 0% {opacity:0;}
34
+ 100% {opacity:1;}
35
+ }
@@ -0,0 +1,219 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { Button, getRedirectUrl, Text } from '@automattic/jetpack-components';
5
+ import { ExternalLink, Modal } from '@wordpress/components';
6
+ import { createInterpolateElement } from '@wordpress/element';
7
+ import { __ } from '@wordpress/i18n';
8
+ import { Icon, chevronRight, external } from '@wordpress/icons';
9
+ import classNames from 'classnames';
10
+ import PropTypes from 'prop-types';
11
+ import React, { useCallback, useState } from 'react';
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import DisconnectDialog from '../disconnect-dialog';
16
+ import './style.scss';
17
+
18
+ /**
19
+ * The RNA Manage Connection Dialog component.
20
+ *
21
+ * @param {object} props -- The properties.
22
+ * @returns {React.Component} The `ManageConnectionDialog` component.
23
+ */
24
+ const ManageConnectionDialog = props => {
25
+ const {
26
+ title,
27
+ apiRoot,
28
+ apiNonce,
29
+ connectedPlugins,
30
+ onDisconnected,
31
+ context,
32
+ connectedUser,
33
+ connectedSiteId,
34
+ isOpen,
35
+ onClose,
36
+ } = props;
37
+
38
+ const [ isDisconnectDialogOpen, setIsDisconnectDialogOpen ] = useState( false );
39
+
40
+ /**
41
+ * Open the Disconnect Dialog.
42
+ */
43
+ const openDisconnectDialog = useCallback(
44
+ e => {
45
+ e && e.preventDefault();
46
+ setIsDisconnectDialogOpen( true );
47
+ },
48
+ [ setIsDisconnectDialogOpen ]
49
+ );
50
+
51
+ /**
52
+ * Close the Disconnect Dialog.
53
+ */
54
+ const closeDisconnectDialog = useCallback(
55
+ e => {
56
+ e && e.preventDefault();
57
+ setIsDisconnectDialogOpen( false );
58
+ },
59
+ [ setIsDisconnectDialogOpen ]
60
+ );
61
+
62
+ return (
63
+ <>
64
+ { isOpen && (
65
+ <>
66
+ <Modal
67
+ title=""
68
+ contentLabel={ title }
69
+ aria={ {
70
+ labelledby: 'jp-connection__manage-dialog__heading',
71
+ } }
72
+ shouldCloseOnClickOutside={ false }
73
+ shouldCloseOnEsc={ false }
74
+ isDismissible={ false }
75
+ className={ 'jp-connection__manage-dialog' }
76
+ >
77
+ <div className="jp-connection__manage-dialog__content">
78
+ <h1 id="jp-connection__manage-dialog__heading">{ title }</h1>
79
+ <Text className="jp-connection__manage-dialog__large-text">
80
+ { __(
81
+ 'At least one user must be connected for your Jetpack products to work properly.',
82
+ 'jetpack'
83
+ ) }
84
+ </Text>
85
+ <ManageConnectionActionCard
86
+ title={ __( 'Transfer ownership to another admin', 'jetpack' ) }
87
+ link={ getRedirectUrl( 'calypso-settings-manage-connection', {
88
+ site: window?.myJetpackInitialState?.siteSuffix,
89
+ } ) }
90
+ key="transfer"
91
+ action="transfer"
92
+ />
93
+ <ManageConnectionActionCard
94
+ title={ __( 'Disconnect Jetpack', 'jetpack' ) }
95
+ onClick={ openDisconnectDialog }
96
+ key="disconnect"
97
+ action="disconnect"
98
+ />
99
+ </div>
100
+ <HelpFooter onClose={ onClose } />
101
+ </Modal>
102
+
103
+ <DisconnectDialog
104
+ apiRoot={ apiRoot }
105
+ apiNonce={ apiNonce }
106
+ onDisconnected={ onDisconnected }
107
+ connectedPlugins={ connectedPlugins }
108
+ connectedSiteId={ connectedSiteId }
109
+ connectedUser={ connectedUser }
110
+ isOpen={ isDisconnectDialogOpen }
111
+ onClose={ closeDisconnectDialog }
112
+ context={ context }
113
+ />
114
+ </>
115
+ ) }
116
+ </>
117
+ );
118
+ };
119
+
120
+ const ManageConnectionActionCard = ( { title, onClick = () => null, link = '#', action } ) => {
121
+ return (
122
+ <div className="jp-connection__manage-dialog__action-card card">
123
+ <div className="jp-connection__manage-dialog__action-card__card-content">
124
+ <a
125
+ href={ link }
126
+ className={ classNames(
127
+ 'jp-connection__manage-dialog__action-card__card-headline',
128
+ action
129
+ ) }
130
+ onClick={ onClick }
131
+ >
132
+ { title }
133
+ <Icon
134
+ icon={ action === 'disconnect' ? chevronRight : external }
135
+ className="jp-connection__manage-dialog__action-card__icon"
136
+ />
137
+ </a>
138
+ </div>
139
+ </div>
140
+ );
141
+ };
142
+
143
+ const HelpFooter = ( { onClose } ) => {
144
+ return (
145
+ <div className="jp-row jp-connection__manage-dialog__actions">
146
+ <div className="jp-connection__manage-dialog__text-wrap lg-col-span-9 md-col-span-7 sm-col-span-3">
147
+ <Text>
148
+ { createInterpolateElement(
149
+ __(
150
+ '<strong>Need help?</strong> Learn more about the <connectionInfoLink>Jetpack connection</connectionInfoLink> or <supportLink>contact Jetpack support</supportLink>',
151
+ 'jetpack'
152
+ ),
153
+ {
154
+ strong: <strong></strong>,
155
+ connectionInfoLink: (
156
+ <ExternalLink
157
+ href={ getRedirectUrl(
158
+ 'why-the-wordpress-com-connection-is-important-for-jetpack'
159
+ ) }
160
+ className="jp-connection__manage-dialog__link"
161
+ // TODO add click track
162
+ />
163
+ ),
164
+ supportLink: (
165
+ <ExternalLink
166
+ href={ getRedirectUrl( 'jetpack-support' ) }
167
+ className="jp-connection__manage-dialog__link"
168
+ // TODO add click track
169
+ />
170
+ ),
171
+ }
172
+ ) }
173
+ </Text>
174
+ </div>
175
+ <div className="jp-connection__manage-dialog__button-wrap lg-col-span-3 md-col-span-1 sm-col-span-1">
176
+ <Button
177
+ weight="regular"
178
+ variant="secondary"
179
+ onClick={ onClose }
180
+ className="jp-connection__manage-dialog__btn-dismiss"
181
+ >
182
+ { __( 'Cancel', 'jetpack' ) }
183
+ </Button>
184
+ </div>
185
+ </div>
186
+ );
187
+ };
188
+
189
+ ManageConnectionDialog.propTypes = {
190
+ /** The modal title. */
191
+ title: PropTypes.string,
192
+ /** API root URL, required. */
193
+ apiRoot: PropTypes.string.isRequired,
194
+ /** API Nonce, required. */
195
+ apiNonce: PropTypes.string.isRequired,
196
+ /** Plugins that are using the Jetpack connection. */
197
+ connectedPlugins: PropTypes.oneOfType( [ PropTypes.array, PropTypes.object ] ),
198
+ /** The callback to be called upon disconnection success. */
199
+ onDisconnected: PropTypes.func,
200
+ /** The context in which this component is being used. */
201
+ context: PropTypes.string,
202
+ /** An object representing the connected user. */
203
+ connectedUser: PropTypes.object,
204
+ /** ID of the currently connected site. */
205
+ connectedSiteId: PropTypes.number,
206
+ /** Whether or not the dialog modal should be open. */
207
+ isOpen: PropTypes.bool,
208
+ /** Callback function for when the modal closes. */
209
+ onClose: PropTypes.func,
210
+ };
211
+
212
+ ManageConnectionDialog.defaultProps = {
213
+ title: __( 'Manage your Jetpack connection', 'jetpack' ),
214
+ isOpen: false,
215
+ context: 'jetpack-dashboard',
216
+ connectedUser: {}, // Pass empty object to avoid undefined errors.
217
+ };
218
+
219
+ export default ManageConnectionDialog;