@atlaskit/feedback-collector 7.1.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 +774 -0
- package/LICENSE +13 -0
- package/README.md +13 -0
- package/dist/cjs/components/FeedbackCollector.js +226 -0
- package/dist/cjs/components/FeedbackFlag.js +35 -0
- package/dist/cjs/components/FeedbackForm.js +227 -0
- package/dist/cjs/index.js +29 -0
- package/dist/cjs/types.js +5 -0
- package/dist/cjs/version.json +5 -0
- package/dist/es2019/components/FeedbackCollector.js +162 -0
- package/dist/es2019/components/FeedbackFlag.js +18 -0
- package/dist/es2019/components/FeedbackForm.js +156 -0
- package/dist/es2019/index.js +5 -0
- package/dist/es2019/types.js +1 -0
- package/dist/es2019/version.json +5 -0
- package/dist/esm/components/FeedbackCollector.js +205 -0
- package/dist/esm/components/FeedbackFlag.js +20 -0
- package/dist/esm/components/FeedbackForm.js +205 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/version.json +5 -0
- package/dist/types/components/FeedbackCollector.d.ts +135 -0
- package/dist/types/components/FeedbackFlag.d.ts +9 -0
- package/dist/types/components/FeedbackForm.d.ts +47 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/types.d.ts +11 -0
- package/package.json +72 -0
- package/types/package.json +7 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
Object.defineProperty(exports, "FeedbackFlag", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: function get() {
|
|
11
|
+
return _FeedbackFlag.default;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
Object.defineProperty(exports, "FeedbackForm", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: function get() {
|
|
17
|
+
return _FeedbackForm.default;
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
exports.default = void 0;
|
|
21
|
+
|
|
22
|
+
var _FeedbackCollector = _interopRequireDefault(require("./components/FeedbackCollector"));
|
|
23
|
+
|
|
24
|
+
var _FeedbackFlag = _interopRequireDefault(require("./components/FeedbackFlag"));
|
|
25
|
+
|
|
26
|
+
var _FeedbackForm = _interopRequireDefault(require("./components/FeedbackForm"));
|
|
27
|
+
|
|
28
|
+
var _default = _FeedbackCollector.default;
|
|
29
|
+
exports.default = _default;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import React, { Component } from 'react';
|
|
3
|
+
import truncate from 'lodash/truncate';
|
|
4
|
+
import FeedbackForm from './FeedbackForm';
|
|
5
|
+
const MAX_SUMMARY_LENGTH_CHARS = 100;
|
|
6
|
+
|
|
7
|
+
const singleLineTruncatedText = (text, length = MAX_SUMMARY_LENGTH_CHARS) => {
|
|
8
|
+
const singleLineText = text.replace(/\n/g, ' ');
|
|
9
|
+
return truncate(singleLineText, {
|
|
10
|
+
length
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default class FeedbackCollector extends Component {
|
|
15
|
+
constructor(...args) {
|
|
16
|
+
super(...args);
|
|
17
|
+
|
|
18
|
+
_defineProperty(this, "postFeedback", formValues => {
|
|
19
|
+
const body = this.mapFormToJSD(formValues); // Don't dispatch unless we have suitable props (allows tests to pass through empty strings and avoid redundant network calls)
|
|
20
|
+
|
|
21
|
+
if (this.props.embeddableKey && this.props.requestTypeId) {
|
|
22
|
+
fetch(`https://jsd-widget.atlassian.com/api/embeddable/${this.props.embeddableKey}/request?requestTypeId=${this.props.requestTypeId}`, {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: {
|
|
25
|
+
'Content-Type': 'application/json'
|
|
26
|
+
},
|
|
27
|
+
body: JSON.stringify(body)
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
this.props.onClose(); // slightly delay confirming submit since we don't wait for the REST call to succeed
|
|
32
|
+
//
|
|
33
|
+
// Because `onClose` is invoked prior to this timeout triggering, the `componentWillUnmount`
|
|
34
|
+
// may occur before the `onSubmit` is called. To prevent prematurely cancelling the
|
|
35
|
+
// network request, we deliberately don't clear this timeout inside `componentWillUnmount`.
|
|
36
|
+
//
|
|
37
|
+
|
|
38
|
+
setTimeout(() => this.props.onSubmit(formValues), this.props.timeoutOnSubmit);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getTypeFieldValue(dtype) {
|
|
43
|
+
switch (dtype) {
|
|
44
|
+
case 'bug':
|
|
45
|
+
return this.props.typeBugDefaultValue;
|
|
46
|
+
|
|
47
|
+
case 'comment':
|
|
48
|
+
return this.props.typeCommentDefaultValue;
|
|
49
|
+
|
|
50
|
+
case 'suggestion':
|
|
51
|
+
return this.props.typeSuggestionDefaultValue;
|
|
52
|
+
|
|
53
|
+
case 'question':
|
|
54
|
+
return this.props.typeQuestionDefaultValue;
|
|
55
|
+
|
|
56
|
+
case 'empty':
|
|
57
|
+
default:
|
|
58
|
+
return this.props.typeEmptyDefaultValue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getEmail(formValues) {
|
|
63
|
+
return formValues.canBeContacted && this.props.email ? this.props.email : this.props.emailDefaultValue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getDescription(formValues) {
|
|
67
|
+
return formValues.description || this.props.descriptionDefaultValue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
getSummary(formValues) {
|
|
71
|
+
return singleLineTruncatedText(formValues.description, this.props.summaryTruncateLength) || this.props.summaryDefaultValue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getCustomerName() {
|
|
75
|
+
return this.props.name || this.props.customerNameDefaultValue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
mapFormToJSD(formValues) {
|
|
79
|
+
return {
|
|
80
|
+
fields: [this.props.showTypeField ? {
|
|
81
|
+
id: this.props.typeFieldId,
|
|
82
|
+
value: this.getTypeFieldValue(formValues.type)
|
|
83
|
+
} : undefined, {
|
|
84
|
+
id: this.props.summaryFieldId,
|
|
85
|
+
value: this.getSummary(formValues)
|
|
86
|
+
}, {
|
|
87
|
+
id: this.props.descriptionFieldId,
|
|
88
|
+
value: this.getDescription(formValues)
|
|
89
|
+
}, {
|
|
90
|
+
id: this.props.emailFieldId,
|
|
91
|
+
value: this.getEmail(formValues)
|
|
92
|
+
}, {
|
|
93
|
+
id: this.props.customerNameFieldId,
|
|
94
|
+
value: this.getCustomerName()
|
|
95
|
+
}, formValues.canBeContacted ? {
|
|
96
|
+
id: this.props.canBeContactedFieldId,
|
|
97
|
+
value: this.props.canBeContactedDefaultValue
|
|
98
|
+
} : undefined, formValues.enrollInResearchGroup ? {
|
|
99
|
+
id: this.props.enrollInResearchFieldId,
|
|
100
|
+
value: this.props.enrollInResearchDefaultValue
|
|
101
|
+
} : undefined, ...this.props.additionalFields].filter(Boolean)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
render() {
|
|
106
|
+
return /*#__PURE__*/React.createElement(FeedbackForm, {
|
|
107
|
+
feedbackTitle: this.props.feedbackTitle,
|
|
108
|
+
feedbackTitleDetails: this.props.feedbackTitleDetails,
|
|
109
|
+
showTypeField: this.props.showTypeField,
|
|
110
|
+
canBeContactedLabel: this.props.canBeContactedLabel,
|
|
111
|
+
enrolInResearchLabel: this.props.enrolInResearchLabel,
|
|
112
|
+
summaryPlaceholder: this.props.summaryPlaceholder,
|
|
113
|
+
submitButtonLabel: this.props.submitButtonLabel,
|
|
114
|
+
cancelButtonLabel: this.props.cancelButtonLabel,
|
|
115
|
+
feedbackGroupLabels: this.props.feedbackGroupLabels,
|
|
116
|
+
onSubmit: this.postFeedback,
|
|
117
|
+
onClose: this.props.onClose
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
_defineProperty(FeedbackCollector, "defaultProps", {
|
|
124
|
+
canBeContactedFieldId: 'customfield_10043',
|
|
125
|
+
canBeContactedDefaultValue: [{
|
|
126
|
+
id: '10109'
|
|
127
|
+
}],
|
|
128
|
+
additionalFields: [],
|
|
129
|
+
customerNameFieldId: 'customfield_10045',
|
|
130
|
+
customerNameDefaultValue: 'unknown',
|
|
131
|
+
descriptionFieldId: 'description',
|
|
132
|
+
descriptionDefaultValue: '',
|
|
133
|
+
enrollInResearchFieldId: 'customfield_10044',
|
|
134
|
+
enrollInResearchDefaultValue: [{
|
|
135
|
+
id: '10110'
|
|
136
|
+
}],
|
|
137
|
+
emailFieldId: 'email',
|
|
138
|
+
emailDefaultValue: 'do-not-reply@atlassian.com',
|
|
139
|
+
summaryFieldId: 'summary',
|
|
140
|
+
summaryDefaultValue: '',
|
|
141
|
+
summaryTruncateLength: 100,
|
|
142
|
+
timeoutOnSubmit: 700,
|
|
143
|
+
typeFieldId: 'customfield_10042',
|
|
144
|
+
typeBugDefaultValue: {
|
|
145
|
+
id: '10105'
|
|
146
|
+
},
|
|
147
|
+
typeCommentDefaultValue: {
|
|
148
|
+
id: '10106'
|
|
149
|
+
},
|
|
150
|
+
typeSuggestionDefaultValue: {
|
|
151
|
+
id: '10107'
|
|
152
|
+
},
|
|
153
|
+
typeQuestionDefaultValue: {
|
|
154
|
+
id: '10108'
|
|
155
|
+
},
|
|
156
|
+
typeEmptyDefaultValue: {
|
|
157
|
+
id: 'empty'
|
|
158
|
+
},
|
|
159
|
+
showTypeField: true,
|
|
160
|
+
onClose: () => {},
|
|
161
|
+
onSubmit: () => {}
|
|
162
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { AutoDismissFlag } from '@atlaskit/flag';
|
|
4
|
+
import SuccessIcon from '@atlaskit/icon/glyph/check-circle';
|
|
5
|
+
import { G300 } from '@atlaskit/theme/colors';
|
|
6
|
+
import { token } from '@atlaskit/tokens';
|
|
7
|
+
|
|
8
|
+
const FeedbackFlag = props => /*#__PURE__*/React.createElement(AutoDismissFlag, _extends({
|
|
9
|
+
icon: /*#__PURE__*/React.createElement(SuccessIcon, {
|
|
10
|
+
primaryColor: token('color.iconBorder.success', G300),
|
|
11
|
+
label: "Success"
|
|
12
|
+
}),
|
|
13
|
+
id: "feedbackSent",
|
|
14
|
+
description: props.description ? props.description : 'Your valuable feedback helps us continually improve our products.',
|
|
15
|
+
title: props.title ? props.title : 'Thanks!'
|
|
16
|
+
}, props));
|
|
17
|
+
|
|
18
|
+
export default FeedbackFlag;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
3
|
+
import React, { Component, Fragment } from 'react';
|
|
4
|
+
import Button from '@atlaskit/button/standard-button';
|
|
5
|
+
import { Checkbox } from '@atlaskit/checkbox';
|
|
6
|
+
import Form, { Field } from '@atlaskit/form';
|
|
7
|
+
import Modal, { ModalBody, ModalFooter, ModalHeader, ModalTitle } from '@atlaskit/modal-dialog';
|
|
8
|
+
import Select from '@atlaskit/select';
|
|
9
|
+
import TextArea from '@atlaskit/textarea';
|
|
10
|
+
|
|
11
|
+
const getFieldLabels = record => {
|
|
12
|
+
return {
|
|
13
|
+
bug: record !== null && record !== void 0 && record.bug.fieldLabel ? record.bug.fieldLabel : 'Describe the bug or issue',
|
|
14
|
+
comment: record !== null && record !== void 0 && record.comment.fieldLabel ? record.comment.fieldLabel : "Let us know what's on your mind",
|
|
15
|
+
suggestion: record !== null && record !== void 0 && record.suggestion.fieldLabel ? record.suggestion.fieldLabel : "Let us know what you'd like to improve",
|
|
16
|
+
question: record !== null && record !== void 0 && record.question.fieldLabel ? record.question.fieldLabel : 'What would you like to know?',
|
|
17
|
+
empty: record !== null && record !== void 0 && record.empty.fieldLabel ? record.empty.fieldLabel : 'Select an option'
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const getSelectOptions = record => {
|
|
22
|
+
return [{
|
|
23
|
+
label: record !== null && record !== void 0 && record.question.selectOptionLabel ? record.question.selectOptionLabel : 'Ask a question',
|
|
24
|
+
value: 'question'
|
|
25
|
+
}, {
|
|
26
|
+
label: record !== null && record !== void 0 && record.comment.selectOptionLabel ? record.comment.selectOptionLabel : 'Leave a comment',
|
|
27
|
+
value: 'comment'
|
|
28
|
+
}, {
|
|
29
|
+
label: record !== null && record !== void 0 && record.bug.selectOptionLabel ? record.bug.selectOptionLabel : 'Report a bug',
|
|
30
|
+
value: 'bug'
|
|
31
|
+
}, {
|
|
32
|
+
label: record !== null && record !== void 0 && record.suggestion.selectOptionLabel ? record.suggestion.selectOptionLabel : 'Suggest an improvement',
|
|
33
|
+
value: 'suggestion'
|
|
34
|
+
}];
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const getDefaultSelectValue = record => {
|
|
38
|
+
return {
|
|
39
|
+
label: record !== null && record !== void 0 && record.empty.selectOptionLabel ? record.empty.selectOptionLabel : 'I want to...',
|
|
40
|
+
value: 'empty'
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default class FeedbackForm extends Component {
|
|
45
|
+
constructor(...args) {
|
|
46
|
+
super(...args);
|
|
47
|
+
|
|
48
|
+
_defineProperty(this, "state", {
|
|
49
|
+
type: 'empty',
|
|
50
|
+
description: '',
|
|
51
|
+
canBeContacted: false,
|
|
52
|
+
enrollInResearchGroup: false
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
_defineProperty(this, "isTypeSelected", () => this.state.type !== 'empty');
|
|
56
|
+
|
|
57
|
+
_defineProperty(this, "onSubmit", () => {
|
|
58
|
+
const {
|
|
59
|
+
type,
|
|
60
|
+
description,
|
|
61
|
+
canBeContacted,
|
|
62
|
+
enrollInResearchGroup
|
|
63
|
+
} = this.state;
|
|
64
|
+
this.props.onSubmit({
|
|
65
|
+
type,
|
|
66
|
+
description,
|
|
67
|
+
canBeContacted,
|
|
68
|
+
enrollInResearchGroup
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
render() {
|
|
74
|
+
const {
|
|
75
|
+
showTypeField
|
|
76
|
+
} = this.props;
|
|
77
|
+
const canShowTextField = this.isTypeSelected() || !showTypeField;
|
|
78
|
+
const isDisabled = showTypeField ? !this.isTypeSelected() || !this.state.description : !this.state.description;
|
|
79
|
+
return /*#__PURE__*/React.createElement(Modal, {
|
|
80
|
+
onClose: this.props.onClose
|
|
81
|
+
}, /*#__PURE__*/React.createElement(Form, {
|
|
82
|
+
onSubmit: this.onSubmit
|
|
83
|
+
}, ({
|
|
84
|
+
formProps
|
|
85
|
+
}) => /*#__PURE__*/React.createElement("form", formProps, /*#__PURE__*/React.createElement(ModalHeader, null, /*#__PURE__*/React.createElement(ModalTitle, null, this.props.feedbackTitle)), /*#__PURE__*/React.createElement(ModalBody, null, this.props.feedbackTitleDetails, showTypeField ? /*#__PURE__*/React.createElement(Select, {
|
|
86
|
+
onChange: option => {
|
|
87
|
+
if (!option || option instanceof Array) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
this.setState({
|
|
92
|
+
type: option.value
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
menuPortalTarget: document.body,
|
|
96
|
+
styles: {
|
|
97
|
+
menuPortal: base => ({ ...base,
|
|
98
|
+
zIndex: 9999
|
|
99
|
+
})
|
|
100
|
+
},
|
|
101
|
+
defaultValue: getDefaultSelectValue(this.props.feedbackGroupLabels),
|
|
102
|
+
options: getSelectOptions(this.props.feedbackGroupLabels)
|
|
103
|
+
}) : null, canShowTextField ? /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(Field, {
|
|
104
|
+
label: showTypeField ? getFieldLabels(this.props.feedbackGroupLabels)[this.state.type] : null,
|
|
105
|
+
isRequired: true,
|
|
106
|
+
name: "description"
|
|
107
|
+
}, ({
|
|
108
|
+
fieldProps
|
|
109
|
+
}) => /*#__PURE__*/React.createElement(TextArea, _extends({}, fieldProps, {
|
|
110
|
+
name: "foo",
|
|
111
|
+
minimumRows: 6,
|
|
112
|
+
placeholder: this.props.summaryPlaceholder,
|
|
113
|
+
onChange: e => this.setState({
|
|
114
|
+
description: e.target.value
|
|
115
|
+
}),
|
|
116
|
+
value: this.state.description
|
|
117
|
+
}))), /*#__PURE__*/React.createElement(Field, {
|
|
118
|
+
name: "can-be-contacted"
|
|
119
|
+
}, ({
|
|
120
|
+
fieldProps
|
|
121
|
+
}) => /*#__PURE__*/React.createElement(Checkbox, _extends({}, fieldProps, {
|
|
122
|
+
label: this.props.canBeContactedLabel,
|
|
123
|
+
onChange: event => this.setState({
|
|
124
|
+
canBeContacted: event.target.checked
|
|
125
|
+
})
|
|
126
|
+
}))), /*#__PURE__*/React.createElement(Field, {
|
|
127
|
+
name: "enroll-in-research-group"
|
|
128
|
+
}, ({
|
|
129
|
+
fieldProps
|
|
130
|
+
}) => /*#__PURE__*/React.createElement(Checkbox, _extends({}, fieldProps, {
|
|
131
|
+
label: this.props.enrolInResearchLabel,
|
|
132
|
+
onChange: event => this.setState({
|
|
133
|
+
enrollInResearchGroup: event.target.checked
|
|
134
|
+
})
|
|
135
|
+
})))) : /*#__PURE__*/React.createElement(Fragment, null)), /*#__PURE__*/React.createElement(ModalFooter, null, /*#__PURE__*/React.createElement(Button, {
|
|
136
|
+
appearance: "subtle",
|
|
137
|
+
onClick: this.props.onClose
|
|
138
|
+
}, this.props.cancelButtonLabel), /*#__PURE__*/React.createElement(Button, {
|
|
139
|
+
appearance: "primary",
|
|
140
|
+
type: "submit",
|
|
141
|
+
isDisabled: isDisabled
|
|
142
|
+
}, this.props.submitButtonLabel)))));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
_defineProperty(FeedbackForm, "defaultProps", {
|
|
148
|
+
showTypeField: true,
|
|
149
|
+
feedbackTitle: 'Share your thoughts',
|
|
150
|
+
enrolInResearchLabel: "I'd like to participate in product research",
|
|
151
|
+
canBeContactedLabel: 'Atlassian can contact me about this feedback',
|
|
152
|
+
submitButtonLabel: 'Send feedback',
|
|
153
|
+
cancelButtonLabel: 'Cancel',
|
|
154
|
+
onClose: () => {},
|
|
155
|
+
onSubmit: () => {}
|
|
156
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
|
+
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
3
|
+
import _createClass from "@babel/runtime/helpers/createClass";
|
|
4
|
+
import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
|
|
5
|
+
import _inherits from "@babel/runtime/helpers/inherits";
|
|
6
|
+
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
|
|
7
|
+
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
|
8
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
9
|
+
|
|
10
|
+
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
|
|
11
|
+
|
|
12
|
+
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
|
|
13
|
+
|
|
14
|
+
import React, { Component } from 'react';
|
|
15
|
+
import truncate from 'lodash/truncate';
|
|
16
|
+
import FeedbackForm from './FeedbackForm';
|
|
17
|
+
var MAX_SUMMARY_LENGTH_CHARS = 100;
|
|
18
|
+
|
|
19
|
+
var singleLineTruncatedText = function singleLineTruncatedText(text) {
|
|
20
|
+
var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : MAX_SUMMARY_LENGTH_CHARS;
|
|
21
|
+
var singleLineText = text.replace(/\n/g, ' ');
|
|
22
|
+
return truncate(singleLineText, {
|
|
23
|
+
length: length
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
var FeedbackCollector = /*#__PURE__*/function (_Component) {
|
|
28
|
+
_inherits(FeedbackCollector, _Component);
|
|
29
|
+
|
|
30
|
+
var _super = _createSuper(FeedbackCollector);
|
|
31
|
+
|
|
32
|
+
function FeedbackCollector() {
|
|
33
|
+
var _this;
|
|
34
|
+
|
|
35
|
+
_classCallCheck(this, FeedbackCollector);
|
|
36
|
+
|
|
37
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
38
|
+
args[_key] = arguments[_key];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_this = _super.call.apply(_super, [this].concat(args));
|
|
42
|
+
|
|
43
|
+
_defineProperty(_assertThisInitialized(_this), "postFeedback", function (formValues) {
|
|
44
|
+
var body = _this.mapFormToJSD(formValues); // Don't dispatch unless we have suitable props (allows tests to pass through empty strings and avoid redundant network calls)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if (_this.props.embeddableKey && _this.props.requestTypeId) {
|
|
48
|
+
fetch("https://jsd-widget.atlassian.com/api/embeddable/".concat(_this.props.embeddableKey, "/request?requestTypeId=").concat(_this.props.requestTypeId), {
|
|
49
|
+
method: 'POST',
|
|
50
|
+
headers: {
|
|
51
|
+
'Content-Type': 'application/json'
|
|
52
|
+
},
|
|
53
|
+
body: JSON.stringify(body)
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
_this.props.onClose(); // slightly delay confirming submit since we don't wait for the REST call to succeed
|
|
58
|
+
//
|
|
59
|
+
// Because `onClose` is invoked prior to this timeout triggering, the `componentWillUnmount`
|
|
60
|
+
// may occur before the `onSubmit` is called. To prevent prematurely cancelling the
|
|
61
|
+
// network request, we deliberately don't clear this timeout inside `componentWillUnmount`.
|
|
62
|
+
//
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
setTimeout(function () {
|
|
66
|
+
return _this.props.onSubmit(formValues);
|
|
67
|
+
}, _this.props.timeoutOnSubmit);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return _this;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_createClass(FeedbackCollector, [{
|
|
74
|
+
key: "getTypeFieldValue",
|
|
75
|
+
value: function getTypeFieldValue(dtype) {
|
|
76
|
+
switch (dtype) {
|
|
77
|
+
case 'bug':
|
|
78
|
+
return this.props.typeBugDefaultValue;
|
|
79
|
+
|
|
80
|
+
case 'comment':
|
|
81
|
+
return this.props.typeCommentDefaultValue;
|
|
82
|
+
|
|
83
|
+
case 'suggestion':
|
|
84
|
+
return this.props.typeSuggestionDefaultValue;
|
|
85
|
+
|
|
86
|
+
case 'question':
|
|
87
|
+
return this.props.typeQuestionDefaultValue;
|
|
88
|
+
|
|
89
|
+
case 'empty':
|
|
90
|
+
default:
|
|
91
|
+
return this.props.typeEmptyDefaultValue;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}, {
|
|
95
|
+
key: "getEmail",
|
|
96
|
+
value: function getEmail(formValues) {
|
|
97
|
+
return formValues.canBeContacted && this.props.email ? this.props.email : this.props.emailDefaultValue;
|
|
98
|
+
}
|
|
99
|
+
}, {
|
|
100
|
+
key: "getDescription",
|
|
101
|
+
value: function getDescription(formValues) {
|
|
102
|
+
return formValues.description || this.props.descriptionDefaultValue;
|
|
103
|
+
}
|
|
104
|
+
}, {
|
|
105
|
+
key: "getSummary",
|
|
106
|
+
value: function getSummary(formValues) {
|
|
107
|
+
return singleLineTruncatedText(formValues.description, this.props.summaryTruncateLength) || this.props.summaryDefaultValue;
|
|
108
|
+
}
|
|
109
|
+
}, {
|
|
110
|
+
key: "getCustomerName",
|
|
111
|
+
value: function getCustomerName() {
|
|
112
|
+
return this.props.name || this.props.customerNameDefaultValue;
|
|
113
|
+
}
|
|
114
|
+
}, {
|
|
115
|
+
key: "mapFormToJSD",
|
|
116
|
+
value: function mapFormToJSD(formValues) {
|
|
117
|
+
return {
|
|
118
|
+
fields: [this.props.showTypeField ? {
|
|
119
|
+
id: this.props.typeFieldId,
|
|
120
|
+
value: this.getTypeFieldValue(formValues.type)
|
|
121
|
+
} : undefined, {
|
|
122
|
+
id: this.props.summaryFieldId,
|
|
123
|
+
value: this.getSummary(formValues)
|
|
124
|
+
}, {
|
|
125
|
+
id: this.props.descriptionFieldId,
|
|
126
|
+
value: this.getDescription(formValues)
|
|
127
|
+
}, {
|
|
128
|
+
id: this.props.emailFieldId,
|
|
129
|
+
value: this.getEmail(formValues)
|
|
130
|
+
}, {
|
|
131
|
+
id: this.props.customerNameFieldId,
|
|
132
|
+
value: this.getCustomerName()
|
|
133
|
+
}, formValues.canBeContacted ? {
|
|
134
|
+
id: this.props.canBeContactedFieldId,
|
|
135
|
+
value: this.props.canBeContactedDefaultValue
|
|
136
|
+
} : undefined, formValues.enrollInResearchGroup ? {
|
|
137
|
+
id: this.props.enrollInResearchFieldId,
|
|
138
|
+
value: this.props.enrollInResearchDefaultValue
|
|
139
|
+
} : undefined].concat(_toConsumableArray(this.props.additionalFields)).filter(Boolean)
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}, {
|
|
143
|
+
key: "render",
|
|
144
|
+
value: function render() {
|
|
145
|
+
return /*#__PURE__*/React.createElement(FeedbackForm, {
|
|
146
|
+
feedbackTitle: this.props.feedbackTitle,
|
|
147
|
+
feedbackTitleDetails: this.props.feedbackTitleDetails,
|
|
148
|
+
showTypeField: this.props.showTypeField,
|
|
149
|
+
canBeContactedLabel: this.props.canBeContactedLabel,
|
|
150
|
+
enrolInResearchLabel: this.props.enrolInResearchLabel,
|
|
151
|
+
summaryPlaceholder: this.props.summaryPlaceholder,
|
|
152
|
+
submitButtonLabel: this.props.submitButtonLabel,
|
|
153
|
+
cancelButtonLabel: this.props.cancelButtonLabel,
|
|
154
|
+
feedbackGroupLabels: this.props.feedbackGroupLabels,
|
|
155
|
+
onSubmit: this.postFeedback,
|
|
156
|
+
onClose: this.props.onClose
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}]);
|
|
160
|
+
|
|
161
|
+
return FeedbackCollector;
|
|
162
|
+
}(Component);
|
|
163
|
+
|
|
164
|
+
_defineProperty(FeedbackCollector, "defaultProps", {
|
|
165
|
+
canBeContactedFieldId: 'customfield_10043',
|
|
166
|
+
canBeContactedDefaultValue: [{
|
|
167
|
+
id: '10109'
|
|
168
|
+
}],
|
|
169
|
+
additionalFields: [],
|
|
170
|
+
customerNameFieldId: 'customfield_10045',
|
|
171
|
+
customerNameDefaultValue: 'unknown',
|
|
172
|
+
descriptionFieldId: 'description',
|
|
173
|
+
descriptionDefaultValue: '',
|
|
174
|
+
enrollInResearchFieldId: 'customfield_10044',
|
|
175
|
+
enrollInResearchDefaultValue: [{
|
|
176
|
+
id: '10110'
|
|
177
|
+
}],
|
|
178
|
+
emailFieldId: 'email',
|
|
179
|
+
emailDefaultValue: 'do-not-reply@atlassian.com',
|
|
180
|
+
summaryFieldId: 'summary',
|
|
181
|
+
summaryDefaultValue: '',
|
|
182
|
+
summaryTruncateLength: 100,
|
|
183
|
+
timeoutOnSubmit: 700,
|
|
184
|
+
typeFieldId: 'customfield_10042',
|
|
185
|
+
typeBugDefaultValue: {
|
|
186
|
+
id: '10105'
|
|
187
|
+
},
|
|
188
|
+
typeCommentDefaultValue: {
|
|
189
|
+
id: '10106'
|
|
190
|
+
},
|
|
191
|
+
typeSuggestionDefaultValue: {
|
|
192
|
+
id: '10107'
|
|
193
|
+
},
|
|
194
|
+
typeQuestionDefaultValue: {
|
|
195
|
+
id: '10108'
|
|
196
|
+
},
|
|
197
|
+
typeEmptyDefaultValue: {
|
|
198
|
+
id: 'empty'
|
|
199
|
+
},
|
|
200
|
+
showTypeField: true,
|
|
201
|
+
onClose: function onClose() {},
|
|
202
|
+
onSubmit: function onSubmit() {}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
export { FeedbackCollector as default };
|