@atlaskit/feedback-collector 14.3.3 → 14.3.4
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 +8 -0
- package/dist/cjs/components/FeedbackCollector.js +1 -1
- package/dist/cjs/components/FeedbackForm.js +68 -10
- package/dist/cjs/messages.js +10 -0
- package/dist/es2019/components/FeedbackCollector.js +1 -1
- package/dist/es2019/components/FeedbackForm.js +66 -14
- package/dist/es2019/messages.js +10 -0
- package/dist/esm/components/FeedbackCollector.js +1 -1
- package/dist/esm/components/FeedbackForm.js +69 -11
- package/dist/esm/messages.js +10 -0
- package/dist/types/messages.d.ts +10 -0
- package/dist/types-ts4.5/messages.d.ts +10 -0
- package/package.json +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @atlaskit/feedback-collector
|
|
2
2
|
|
|
3
|
+
## 14.3.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`94ba8d27dba35`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/94ba8d27dba35) -
|
|
8
|
+
[ux] enable the submit button by default for a11y, validate and prevent submission if required
|
|
9
|
+
fields are not complete
|
|
10
|
+
|
|
3
11
|
## 14.3.3
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
|
@@ -163,7 +163,7 @@ var FeedbackCollector = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
163
163
|
}, {
|
|
164
164
|
key: "getPackageVersion",
|
|
165
165
|
value: function getPackageVersion() {
|
|
166
|
-
return "14.3.
|
|
166
|
+
return "14.3.3" || 'Unknown, at least 11.0.0';
|
|
167
167
|
}
|
|
168
168
|
}, {
|
|
169
169
|
key: "getEntitlementInformation",
|
|
@@ -19,6 +19,7 @@ var _checkbox = require("@atlaskit/checkbox");
|
|
|
19
19
|
var _form = _interopRequireWildcard(require("@atlaskit/form"));
|
|
20
20
|
var _link = _interopRequireDefault(require("@atlaskit/link"));
|
|
21
21
|
var _modalDialog = _interopRequireWildcard(require("@atlaskit/modal-dialog"));
|
|
22
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
22
23
|
var _sectionMessage = _interopRequireDefault(require("@atlaskit/section-message"));
|
|
23
24
|
var _select = _interopRequireDefault(require("@atlaskit/select"));
|
|
24
25
|
var _textarea = _interopRequireDefault(require("@atlaskit/textarea"));
|
|
@@ -74,6 +75,10 @@ var FeedbackForm = function FeedbackForm(_ref) {
|
|
|
74
75
|
_useState0 = (0, _slicedToArray2.default)(_useState9, 2),
|
|
75
76
|
isSubmitting = _useState0[0],
|
|
76
77
|
setIsSubmitting = _useState0[1];
|
|
78
|
+
var _useState1 = (0, _react.useState)({}),
|
|
79
|
+
_useState10 = (0, _slicedToArray2.default)(_useState1, 2),
|
|
80
|
+
validationErrors = _useState10[0],
|
|
81
|
+
setValidationErrors = _useState10[1];
|
|
77
82
|
var _useIntl = (0, _reactIntlNext.useIntl)(),
|
|
78
83
|
formatMessage = _useIntl.formatMessage;
|
|
79
84
|
var isTypeSelected = function isTypeSelected() {
|
|
@@ -81,7 +86,26 @@ var FeedbackForm = function FeedbackForm(_ref) {
|
|
|
81
86
|
};
|
|
82
87
|
var canShowTextField = isTypeSelected() || !showTypeField;
|
|
83
88
|
var hasDescription = description || hasDescriptionDefaultValue;
|
|
84
|
-
|
|
89
|
+
|
|
90
|
+
// Feature flag determines validation behavior
|
|
91
|
+
var useNewValidation = (0, _platformFeatureFlags.fg)('feedback-collector-custom-validation');
|
|
92
|
+
var isDisabled = useNewValidation ? isSubmitting || disableSubmitButton // New: only disable when submitting or explicitly disabled
|
|
93
|
+
: disableSubmitButton || (showTypeField ? !isTypeSelected() || !hasDescription : !hasDescription); // Old: disable based on form validation
|
|
94
|
+
|
|
95
|
+
var getValidationErrors = function getValidationErrors() {
|
|
96
|
+
var errors = {};
|
|
97
|
+
|
|
98
|
+
// Validate type selection if showTypeField is true
|
|
99
|
+
if (showTypeField && !isTypeSelected()) {
|
|
100
|
+
errors.type = formatMessage(_messages.messages.validationErrorTypeRequired);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Validate description if showDefaultTextFields is true
|
|
104
|
+
if (showDefaultTextFields && !hasDescription) {
|
|
105
|
+
errors.description = formatMessage(_messages.messages.validationErrorDescriptionRequired);
|
|
106
|
+
}
|
|
107
|
+
return errors;
|
|
108
|
+
};
|
|
85
109
|
var getFieldLabels = function getFieldLabels(record) {
|
|
86
110
|
var _record$bug, _record$comment, _record$suggestion, _record$question, _record$empty, _record$not_relevant, _record$not_accurate, _record$too_slow, _record$unhelpful_lin, _record$other;
|
|
87
111
|
return {
|
|
@@ -127,24 +151,42 @@ var FeedbackForm = function FeedbackForm(_ref) {
|
|
|
127
151
|
shouldScrollInViewport: true
|
|
128
152
|
}, /*#__PURE__*/_react.default.createElement(_form.default, {
|
|
129
153
|
onSubmit: /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
|
|
154
|
+
var errors;
|
|
130
155
|
return _regenerator.default.wrap(function _callee$(_context) {
|
|
131
156
|
while (1) switch (_context.prev = _context.next) {
|
|
132
157
|
case 0:
|
|
158
|
+
if (!useNewValidation) {
|
|
159
|
+
_context.next = 5;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
// New validation: validate on submit and show errors
|
|
163
|
+
errors = getValidationErrors(); // If there are validation errors, show them and don't submit
|
|
164
|
+
if (!(Object.keys(errors).length > 0)) {
|
|
165
|
+
_context.next = 5;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
setValidationErrors(errors);
|
|
169
|
+
return _context.abrupt("return");
|
|
170
|
+
case 5:
|
|
171
|
+
// Submit the form (both old and new validation paths reach here)
|
|
133
172
|
setIsSubmitting(true);
|
|
134
|
-
_context.
|
|
173
|
+
_context.prev = 6;
|
|
174
|
+
_context.next = 9;
|
|
135
175
|
return onSubmit({
|
|
136
176
|
canBeContacted: canBeContacted,
|
|
137
177
|
description: description,
|
|
138
178
|
enrollInResearchGroup: enrollInResearchGroup,
|
|
139
179
|
type: type
|
|
140
180
|
});
|
|
141
|
-
case
|
|
181
|
+
case 9:
|
|
182
|
+
_context.prev = 9;
|
|
142
183
|
setIsSubmitting(false);
|
|
143
|
-
|
|
184
|
+
return _context.finish(9);
|
|
185
|
+
case 12:
|
|
144
186
|
case "end":
|
|
145
187
|
return _context.stop();
|
|
146
188
|
}
|
|
147
|
-
}, _callee);
|
|
189
|
+
}, _callee, null, [[6,, 9, 12]]);
|
|
148
190
|
}))
|
|
149
191
|
}, function (_ref3) {
|
|
150
192
|
var formProps = _ref3.formProps;
|
|
@@ -166,12 +208,20 @@ var FeedbackForm = function FeedbackForm(_ref) {
|
|
|
166
208
|
var _ref4$fieldProps = _ref4.fieldProps,
|
|
167
209
|
id = _ref4$fieldProps.id,
|
|
168
210
|
restProps = (0, _objectWithoutProperties2.default)(_ref4$fieldProps, _excluded);
|
|
169
|
-
return /*#__PURE__*/_react.default.createElement(_select.default, (0, _extends2.default)({}, restProps, {
|
|
211
|
+
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_select.default, (0, _extends2.default)({}, restProps, {
|
|
170
212
|
onChange: function onChange(option) {
|
|
171
213
|
if (!option || option instanceof Array) {
|
|
172
214
|
return;
|
|
173
215
|
}
|
|
174
216
|
setType(option.value);
|
|
217
|
+
// Clear validation error when user selects a type (only for new validation)
|
|
218
|
+
if (useNewValidation && validationErrors.type) {
|
|
219
|
+
setValidationErrors(function (prev) {
|
|
220
|
+
return _objectSpread(_objectSpread({}, prev), {}, {
|
|
221
|
+
type: undefined
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
}
|
|
175
225
|
},
|
|
176
226
|
menuPortalTarget: document.body,
|
|
177
227
|
styles: {
|
|
@@ -187,22 +237,30 @@ var FeedbackForm = function FeedbackForm(_ref) {
|
|
|
187
237
|
ref: focusRef,
|
|
188
238
|
placeholder: getDefaultPlaceholder(feedbackGroupLabels),
|
|
189
239
|
inputId: id
|
|
190
|
-
}));
|
|
240
|
+
})), useNewValidation && validationErrors.type && /*#__PURE__*/_react.default.createElement(_form.ErrorMessage, null, validationErrors.type));
|
|
191
241
|
}) : null, showDefaultTextFields && canShowTextField && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_form.Field, {
|
|
192
242
|
label: showTypeField ? getFieldLabels(feedbackGroupLabels)[type] : customTextAreaLabel || formatMessage(_messages.messages.defaultCustomTextAreaLabel),
|
|
193
243
|
isRequired: true,
|
|
194
244
|
name: "description"
|
|
195
245
|
}, function (_ref5) {
|
|
196
246
|
var fieldProps = _ref5.fieldProps;
|
|
197
|
-
return /*#__PURE__*/_react.default.createElement(_textarea.default, (0, _extends2.default)({}, fieldProps, {
|
|
247
|
+
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_textarea.default, (0, _extends2.default)({}, fieldProps, {
|
|
198
248
|
name: "foo",
|
|
199
249
|
minimumRows: 6,
|
|
200
250
|
placeholder: summaryPlaceholder || undefined,
|
|
201
251
|
onChange: function onChange(e) {
|
|
202
|
-
|
|
252
|
+
setDescription(e.target.value);
|
|
253
|
+
// Clear validation error when user types
|
|
254
|
+
if (useNewValidation && validationErrors.description) {
|
|
255
|
+
setValidationErrors(function (prev) {
|
|
256
|
+
return _objectSpread(_objectSpread({}, prev), {}, {
|
|
257
|
+
description: undefined
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
}
|
|
203
261
|
},
|
|
204
262
|
value: description
|
|
205
|
-
}));
|
|
263
|
+
})), useNewValidation && validationErrors.description && /*#__PURE__*/_react.default.createElement(_form.ErrorMessage, null, validationErrors.description));
|
|
206
264
|
}), !anonymousFeedback && /*#__PURE__*/_react.default.createElement(_form.Fieldset, null, /*#__PURE__*/_react.default.createElement("legend", {
|
|
207
265
|
"aria-hidden": false,
|
|
208
266
|
hidden: true
|
package/dist/cjs/messages.js
CHANGED
|
@@ -155,5 +155,15 @@ var messages = exports.messages = (0, _reactIntlNext.defineMessages)({
|
|
|
155
155
|
id: 'feedback-collector.default.custom.textarea.label',
|
|
156
156
|
defaultMessage: "What's on your mind?",
|
|
157
157
|
description: 'The textarea label where users can write their suggestion for custom feedback collector'
|
|
158
|
+
},
|
|
159
|
+
validationErrorTypeRequired: {
|
|
160
|
+
id: 'feedback-collector.validation.type.required',
|
|
161
|
+
defaultMessage: 'Please select a feedback type',
|
|
162
|
+
description: 'Error message when feedback type is not selected'
|
|
163
|
+
},
|
|
164
|
+
validationErrorDescriptionRequired: {
|
|
165
|
+
id: 'feedback-collector.validation.description.required',
|
|
166
|
+
defaultMessage: 'Please provide a description',
|
|
167
|
+
description: 'Error message when description is not provided'
|
|
158
168
|
}
|
|
159
169
|
});
|
|
@@ -91,7 +91,7 @@ export default class FeedbackCollector extends Component {
|
|
|
91
91
|
return FeedbackCollector.defaultProps.url;
|
|
92
92
|
}
|
|
93
93
|
getPackageVersion() {
|
|
94
|
-
return "14.3.
|
|
94
|
+
return "14.3.3" || 'Unknown, at least 11.0.0';
|
|
95
95
|
}
|
|
96
96
|
async getEntitlementInformation() {
|
|
97
97
|
var _entitlementDetails, _entitlementDetails2, _productName, _entitlement, _productEntitlement;
|
|
@@ -3,9 +3,10 @@ import React, { useRef, useState } from 'react';
|
|
|
3
3
|
import { FormattedMessage, useIntl } from 'react-intl-next';
|
|
4
4
|
import Button from '@atlaskit/button/standard-button';
|
|
5
5
|
import { Checkbox } from '@atlaskit/checkbox';
|
|
6
|
-
import Form, { Field, Fieldset, RequiredAsterisk } from '@atlaskit/form';
|
|
6
|
+
import Form, { ErrorMessage, Field, Fieldset, RequiredAsterisk } from '@atlaskit/form';
|
|
7
7
|
import Link from '@atlaskit/link';
|
|
8
8
|
import Modal, { ModalBody, ModalFooter, ModalHeader, ModalTitle } from '@atlaskit/modal-dialog';
|
|
9
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
9
10
|
import SectionMessage from '@atlaskit/section-message';
|
|
10
11
|
import Select from '@atlaskit/select';
|
|
11
12
|
import TextArea from '@atlaskit/textarea';
|
|
@@ -39,13 +40,33 @@ const FeedbackForm = ({
|
|
|
39
40
|
const [enrollInResearchGroup, setEnrollInResearchGroup] = useState(false);
|
|
40
41
|
const [type, setType] = useState('empty');
|
|
41
42
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
43
|
+
const [validationErrors, setValidationErrors] = useState({});
|
|
42
44
|
const {
|
|
43
45
|
formatMessage
|
|
44
46
|
} = useIntl();
|
|
45
47
|
const isTypeSelected = () => type !== 'empty';
|
|
46
48
|
const canShowTextField = isTypeSelected() || !showTypeField;
|
|
47
49
|
const hasDescription = description || hasDescriptionDefaultValue;
|
|
48
|
-
|
|
50
|
+
|
|
51
|
+
// Feature flag determines validation behavior
|
|
52
|
+
const useNewValidation = fg('feedback-collector-custom-validation');
|
|
53
|
+
const isDisabled = useNewValidation ? isSubmitting || disableSubmitButton // New: only disable when submitting or explicitly disabled
|
|
54
|
+
: disableSubmitButton || (showTypeField ? !isTypeSelected() || !hasDescription : !hasDescription); // Old: disable based on form validation
|
|
55
|
+
|
|
56
|
+
const getValidationErrors = () => {
|
|
57
|
+
const errors = {};
|
|
58
|
+
|
|
59
|
+
// Validate type selection if showTypeField is true
|
|
60
|
+
if (showTypeField && !isTypeSelected()) {
|
|
61
|
+
errors.type = formatMessage(messages.validationErrorTypeRequired);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Validate description if showDefaultTextFields is true
|
|
65
|
+
if (showDefaultTextFields && !hasDescription) {
|
|
66
|
+
errors.description = formatMessage(messages.validationErrorDescriptionRequired);
|
|
67
|
+
}
|
|
68
|
+
return errors;
|
|
69
|
+
};
|
|
49
70
|
const getFieldLabels = record => {
|
|
50
71
|
var _record$bug, _record$comment, _record$suggestion, _record$question, _record$empty, _record$not_relevant, _record$not_accurate, _record$too_slow, _record$unhelpful_lin, _record$other;
|
|
51
72
|
return {
|
|
@@ -91,14 +112,29 @@ const FeedbackForm = ({
|
|
|
91
112
|
shouldScrollInViewport: true
|
|
92
113
|
}, /*#__PURE__*/React.createElement(Form, {
|
|
93
114
|
onSubmit: async () => {
|
|
115
|
+
if (useNewValidation) {
|
|
116
|
+
// New validation: validate on submit and show errors
|
|
117
|
+
const errors = getValidationErrors();
|
|
118
|
+
|
|
119
|
+
// If there are validation errors, show them and don't submit
|
|
120
|
+
if (Object.keys(errors).length > 0) {
|
|
121
|
+
setValidationErrors(errors);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Submit the form (both old and new validation paths reach here)
|
|
94
127
|
setIsSubmitting(true);
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
128
|
+
try {
|
|
129
|
+
await onSubmit({
|
|
130
|
+
canBeContacted,
|
|
131
|
+
description,
|
|
132
|
+
enrollInResearchGroup,
|
|
133
|
+
type
|
|
134
|
+
});
|
|
135
|
+
} finally {
|
|
136
|
+
setIsSubmitting(false);
|
|
137
|
+
}
|
|
102
138
|
}
|
|
103
139
|
}, ({
|
|
104
140
|
formProps
|
|
@@ -121,12 +157,19 @@ const FeedbackForm = ({
|
|
|
121
157
|
id,
|
|
122
158
|
...restProps
|
|
123
159
|
}
|
|
124
|
-
}) => /*#__PURE__*/React.createElement(Select, _extends({}, restProps, {
|
|
160
|
+
}) => /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Select, _extends({}, restProps, {
|
|
125
161
|
onChange: option => {
|
|
126
162
|
if (!option || option instanceof Array) {
|
|
127
163
|
return;
|
|
128
164
|
}
|
|
129
165
|
setType(option.value);
|
|
166
|
+
// Clear validation error when user selects a type (only for new validation)
|
|
167
|
+
if (useNewValidation && validationErrors.type) {
|
|
168
|
+
setValidationErrors(prev => ({
|
|
169
|
+
...prev,
|
|
170
|
+
type: undefined
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
130
173
|
},
|
|
131
174
|
menuPortalTarget: document.body,
|
|
132
175
|
styles: {
|
|
@@ -141,19 +184,28 @@ const FeedbackForm = ({
|
|
|
141
184
|
ref: focusRef,
|
|
142
185
|
placeholder: getDefaultPlaceholder(feedbackGroupLabels),
|
|
143
186
|
inputId: id
|
|
144
|
-
}))) : null, showDefaultTextFields && canShowTextField && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Field, {
|
|
187
|
+
})), useNewValidation && validationErrors.type && /*#__PURE__*/React.createElement(ErrorMessage, null, validationErrors.type))) : null, showDefaultTextFields && canShowTextField && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Field, {
|
|
145
188
|
label: showTypeField ? getFieldLabels(feedbackGroupLabels)[type] : customTextAreaLabel || formatMessage(messages.defaultCustomTextAreaLabel),
|
|
146
189
|
isRequired: true,
|
|
147
190
|
name: "description"
|
|
148
191
|
}, ({
|
|
149
192
|
fieldProps
|
|
150
|
-
}) => /*#__PURE__*/React.createElement(TextArea, _extends({}, fieldProps, {
|
|
193
|
+
}) => /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(TextArea, _extends({}, fieldProps, {
|
|
151
194
|
name: "foo",
|
|
152
195
|
minimumRows: 6,
|
|
153
196
|
placeholder: summaryPlaceholder || undefined,
|
|
154
|
-
onChange: e =>
|
|
197
|
+
onChange: e => {
|
|
198
|
+
setDescription(e.target.value);
|
|
199
|
+
// Clear validation error when user types
|
|
200
|
+
if (useNewValidation && validationErrors.description) {
|
|
201
|
+
setValidationErrors(prev => ({
|
|
202
|
+
...prev,
|
|
203
|
+
description: undefined
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
},
|
|
155
207
|
value: description
|
|
156
|
-
}))), !anonymousFeedback && /*#__PURE__*/React.createElement(Fieldset, null, /*#__PURE__*/React.createElement("legend", {
|
|
208
|
+
})), useNewValidation && validationErrors.description && /*#__PURE__*/React.createElement(ErrorMessage, null, validationErrors.description))), !anonymousFeedback && /*#__PURE__*/React.createElement(Fieldset, null, /*#__PURE__*/React.createElement("legend", {
|
|
157
209
|
"aria-hidden": false,
|
|
158
210
|
hidden: true
|
|
159
211
|
}, "Atlassian opt-in options"), /*#__PURE__*/React.createElement(Field, {
|
package/dist/es2019/messages.js
CHANGED
|
@@ -149,5 +149,15 @@ export const messages = defineMessages({
|
|
|
149
149
|
id: 'feedback-collector.default.custom.textarea.label',
|
|
150
150
|
defaultMessage: "What's on your mind?",
|
|
151
151
|
description: 'The textarea label where users can write their suggestion for custom feedback collector'
|
|
152
|
+
},
|
|
153
|
+
validationErrorTypeRequired: {
|
|
154
|
+
id: 'feedback-collector.validation.type.required',
|
|
155
|
+
defaultMessage: 'Please select a feedback type',
|
|
156
|
+
description: 'Error message when feedback type is not selected'
|
|
157
|
+
},
|
|
158
|
+
validationErrorDescriptionRequired: {
|
|
159
|
+
id: 'feedback-collector.validation.description.required',
|
|
160
|
+
defaultMessage: 'Please provide a description',
|
|
161
|
+
description: 'Error message when description is not provided'
|
|
152
162
|
}
|
|
153
163
|
});
|
|
@@ -154,7 +154,7 @@ var FeedbackCollector = /*#__PURE__*/function (_Component) {
|
|
|
154
154
|
}, {
|
|
155
155
|
key: "getPackageVersion",
|
|
156
156
|
value: function getPackageVersion() {
|
|
157
|
-
return "14.3.
|
|
157
|
+
return "14.3.3" || 'Unknown, at least 11.0.0';
|
|
158
158
|
}
|
|
159
159
|
}, {
|
|
160
160
|
key: "getEntitlementInformation",
|
|
@@ -12,9 +12,10 @@ import React, { useRef, useState } from 'react';
|
|
|
12
12
|
import { FormattedMessage, useIntl } from 'react-intl-next';
|
|
13
13
|
import Button from '@atlaskit/button/standard-button';
|
|
14
14
|
import { Checkbox } from '@atlaskit/checkbox';
|
|
15
|
-
import Form, { Field, Fieldset, RequiredAsterisk } from '@atlaskit/form';
|
|
15
|
+
import Form, { ErrorMessage, Field, Fieldset, RequiredAsterisk } from '@atlaskit/form';
|
|
16
16
|
import Link from '@atlaskit/link';
|
|
17
17
|
import Modal, { ModalBody, ModalFooter, ModalHeader, ModalTitle } from '@atlaskit/modal-dialog';
|
|
18
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
18
19
|
import SectionMessage from '@atlaskit/section-message';
|
|
19
20
|
import Select from '@atlaskit/select';
|
|
20
21
|
import TextArea from '@atlaskit/textarea';
|
|
@@ -65,6 +66,10 @@ var FeedbackForm = function FeedbackForm(_ref) {
|
|
|
65
66
|
_useState0 = _slicedToArray(_useState9, 2),
|
|
66
67
|
isSubmitting = _useState0[0],
|
|
67
68
|
setIsSubmitting = _useState0[1];
|
|
69
|
+
var _useState1 = useState({}),
|
|
70
|
+
_useState10 = _slicedToArray(_useState1, 2),
|
|
71
|
+
validationErrors = _useState10[0],
|
|
72
|
+
setValidationErrors = _useState10[1];
|
|
68
73
|
var _useIntl = useIntl(),
|
|
69
74
|
formatMessage = _useIntl.formatMessage;
|
|
70
75
|
var isTypeSelected = function isTypeSelected() {
|
|
@@ -72,7 +77,26 @@ var FeedbackForm = function FeedbackForm(_ref) {
|
|
|
72
77
|
};
|
|
73
78
|
var canShowTextField = isTypeSelected() || !showTypeField;
|
|
74
79
|
var hasDescription = description || hasDescriptionDefaultValue;
|
|
75
|
-
|
|
80
|
+
|
|
81
|
+
// Feature flag determines validation behavior
|
|
82
|
+
var useNewValidation = fg('feedback-collector-custom-validation');
|
|
83
|
+
var isDisabled = useNewValidation ? isSubmitting || disableSubmitButton // New: only disable when submitting or explicitly disabled
|
|
84
|
+
: disableSubmitButton || (showTypeField ? !isTypeSelected() || !hasDescription : !hasDescription); // Old: disable based on form validation
|
|
85
|
+
|
|
86
|
+
var getValidationErrors = function getValidationErrors() {
|
|
87
|
+
var errors = {};
|
|
88
|
+
|
|
89
|
+
// Validate type selection if showTypeField is true
|
|
90
|
+
if (showTypeField && !isTypeSelected()) {
|
|
91
|
+
errors.type = formatMessage(messages.validationErrorTypeRequired);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Validate description if showDefaultTextFields is true
|
|
95
|
+
if (showDefaultTextFields && !hasDescription) {
|
|
96
|
+
errors.description = formatMessage(messages.validationErrorDescriptionRequired);
|
|
97
|
+
}
|
|
98
|
+
return errors;
|
|
99
|
+
};
|
|
76
100
|
var getFieldLabels = function getFieldLabels(record) {
|
|
77
101
|
var _record$bug, _record$comment, _record$suggestion, _record$question, _record$empty, _record$not_relevant, _record$not_accurate, _record$too_slow, _record$unhelpful_lin, _record$other;
|
|
78
102
|
return {
|
|
@@ -118,24 +142,42 @@ var FeedbackForm = function FeedbackForm(_ref) {
|
|
|
118
142
|
shouldScrollInViewport: true
|
|
119
143
|
}, /*#__PURE__*/React.createElement(Form, {
|
|
120
144
|
onSubmit: /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
|
|
145
|
+
var errors;
|
|
121
146
|
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
122
147
|
while (1) switch (_context.prev = _context.next) {
|
|
123
148
|
case 0:
|
|
149
|
+
if (!useNewValidation) {
|
|
150
|
+
_context.next = 5;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
// New validation: validate on submit and show errors
|
|
154
|
+
errors = getValidationErrors(); // If there are validation errors, show them and don't submit
|
|
155
|
+
if (!(Object.keys(errors).length > 0)) {
|
|
156
|
+
_context.next = 5;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
setValidationErrors(errors);
|
|
160
|
+
return _context.abrupt("return");
|
|
161
|
+
case 5:
|
|
162
|
+
// Submit the form (both old and new validation paths reach here)
|
|
124
163
|
setIsSubmitting(true);
|
|
125
|
-
_context.
|
|
164
|
+
_context.prev = 6;
|
|
165
|
+
_context.next = 9;
|
|
126
166
|
return onSubmit({
|
|
127
167
|
canBeContacted: canBeContacted,
|
|
128
168
|
description: description,
|
|
129
169
|
enrollInResearchGroup: enrollInResearchGroup,
|
|
130
170
|
type: type
|
|
131
171
|
});
|
|
132
|
-
case
|
|
172
|
+
case 9:
|
|
173
|
+
_context.prev = 9;
|
|
133
174
|
setIsSubmitting(false);
|
|
134
|
-
|
|
175
|
+
return _context.finish(9);
|
|
176
|
+
case 12:
|
|
135
177
|
case "end":
|
|
136
178
|
return _context.stop();
|
|
137
179
|
}
|
|
138
|
-
}, _callee);
|
|
180
|
+
}, _callee, null, [[6,, 9, 12]]);
|
|
139
181
|
}))
|
|
140
182
|
}, function (_ref3) {
|
|
141
183
|
var formProps = _ref3.formProps;
|
|
@@ -157,12 +199,20 @@ var FeedbackForm = function FeedbackForm(_ref) {
|
|
|
157
199
|
var _ref4$fieldProps = _ref4.fieldProps,
|
|
158
200
|
id = _ref4$fieldProps.id,
|
|
159
201
|
restProps = _objectWithoutProperties(_ref4$fieldProps, _excluded);
|
|
160
|
-
return /*#__PURE__*/React.createElement(Select, _extends({}, restProps, {
|
|
202
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Select, _extends({}, restProps, {
|
|
161
203
|
onChange: function onChange(option) {
|
|
162
204
|
if (!option || option instanceof Array) {
|
|
163
205
|
return;
|
|
164
206
|
}
|
|
165
207
|
setType(option.value);
|
|
208
|
+
// Clear validation error when user selects a type (only for new validation)
|
|
209
|
+
if (useNewValidation && validationErrors.type) {
|
|
210
|
+
setValidationErrors(function (prev) {
|
|
211
|
+
return _objectSpread(_objectSpread({}, prev), {}, {
|
|
212
|
+
type: undefined
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
}
|
|
166
216
|
},
|
|
167
217
|
menuPortalTarget: document.body,
|
|
168
218
|
styles: {
|
|
@@ -178,22 +228,30 @@ var FeedbackForm = function FeedbackForm(_ref) {
|
|
|
178
228
|
ref: focusRef,
|
|
179
229
|
placeholder: getDefaultPlaceholder(feedbackGroupLabels),
|
|
180
230
|
inputId: id
|
|
181
|
-
}));
|
|
231
|
+
})), useNewValidation && validationErrors.type && /*#__PURE__*/React.createElement(ErrorMessage, null, validationErrors.type));
|
|
182
232
|
}) : null, showDefaultTextFields && canShowTextField && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Field, {
|
|
183
233
|
label: showTypeField ? getFieldLabels(feedbackGroupLabels)[type] : customTextAreaLabel || formatMessage(messages.defaultCustomTextAreaLabel),
|
|
184
234
|
isRequired: true,
|
|
185
235
|
name: "description"
|
|
186
236
|
}, function (_ref5) {
|
|
187
237
|
var fieldProps = _ref5.fieldProps;
|
|
188
|
-
return /*#__PURE__*/React.createElement(TextArea, _extends({}, fieldProps, {
|
|
238
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(TextArea, _extends({}, fieldProps, {
|
|
189
239
|
name: "foo",
|
|
190
240
|
minimumRows: 6,
|
|
191
241
|
placeholder: summaryPlaceholder || undefined,
|
|
192
242
|
onChange: function onChange(e) {
|
|
193
|
-
|
|
243
|
+
setDescription(e.target.value);
|
|
244
|
+
// Clear validation error when user types
|
|
245
|
+
if (useNewValidation && validationErrors.description) {
|
|
246
|
+
setValidationErrors(function (prev) {
|
|
247
|
+
return _objectSpread(_objectSpread({}, prev), {}, {
|
|
248
|
+
description: undefined
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
}
|
|
194
252
|
},
|
|
195
253
|
value: description
|
|
196
|
-
}));
|
|
254
|
+
})), useNewValidation && validationErrors.description && /*#__PURE__*/React.createElement(ErrorMessage, null, validationErrors.description));
|
|
197
255
|
}), !anonymousFeedback && /*#__PURE__*/React.createElement(Fieldset, null, /*#__PURE__*/React.createElement("legend", {
|
|
198
256
|
"aria-hidden": false,
|
|
199
257
|
hidden: true
|
package/dist/esm/messages.js
CHANGED
|
@@ -149,5 +149,15 @@ export var messages = defineMessages({
|
|
|
149
149
|
id: 'feedback-collector.default.custom.textarea.label',
|
|
150
150
|
defaultMessage: "What's on your mind?",
|
|
151
151
|
description: 'The textarea label where users can write their suggestion for custom feedback collector'
|
|
152
|
+
},
|
|
153
|
+
validationErrorTypeRequired: {
|
|
154
|
+
id: 'feedback-collector.validation.type.required',
|
|
155
|
+
defaultMessage: 'Please select a feedback type',
|
|
156
|
+
description: 'Error message when feedback type is not selected'
|
|
157
|
+
},
|
|
158
|
+
validationErrorDescriptionRequired: {
|
|
159
|
+
id: 'feedback-collector.validation.description.required',
|
|
160
|
+
defaultMessage: 'Please provide a description',
|
|
161
|
+
description: 'Error message when description is not provided'
|
|
152
162
|
}
|
|
153
163
|
});
|
package/dist/types/messages.d.ts
CHANGED
|
@@ -149,4 +149,14 @@ export declare const messages: {
|
|
|
149
149
|
defaultMessage: string;
|
|
150
150
|
description: string;
|
|
151
151
|
};
|
|
152
|
+
validationErrorTypeRequired: {
|
|
153
|
+
id: string;
|
|
154
|
+
defaultMessage: string;
|
|
155
|
+
description: string;
|
|
156
|
+
};
|
|
157
|
+
validationErrorDescriptionRequired: {
|
|
158
|
+
id: string;
|
|
159
|
+
defaultMessage: string;
|
|
160
|
+
description: string;
|
|
161
|
+
};
|
|
152
162
|
};
|
|
@@ -149,4 +149,14 @@ export declare const messages: {
|
|
|
149
149
|
defaultMessage: string;
|
|
150
150
|
description: string;
|
|
151
151
|
};
|
|
152
|
+
validationErrorTypeRequired: {
|
|
153
|
+
id: string;
|
|
154
|
+
defaultMessage: string;
|
|
155
|
+
description: string;
|
|
156
|
+
};
|
|
157
|
+
validationErrorDescriptionRequired: {
|
|
158
|
+
id: string;
|
|
159
|
+
defaultMessage: string;
|
|
160
|
+
description: string;
|
|
161
|
+
};
|
|
152
162
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/feedback-collector",
|
|
3
|
-
"version": "14.3.
|
|
3
|
+
"version": "14.3.4",
|
|
4
4
|
"description": "A component that collects feedback across Atlassian products.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@atlaskit/button": "^23.4.0",
|
|
41
41
|
"@atlaskit/checkbox": "^17.1.0",
|
|
42
42
|
"@atlaskit/flag": "^17.3.0",
|
|
43
|
-
"@atlaskit/form": "^12.
|
|
43
|
+
"@atlaskit/form": "^12.2.0",
|
|
44
44
|
"@atlaskit/icon": "^28.0.0",
|
|
45
45
|
"@atlaskit/link": "^3.2.0",
|
|
46
46
|
"@atlaskit/modal-dialog": "^14.3.0",
|
|
@@ -90,6 +90,9 @@
|
|
|
90
90
|
},
|
|
91
91
|
"dst-a11y__replace-anchor-with-link__belugas-feedba": {
|
|
92
92
|
"type": "boolean"
|
|
93
|
+
},
|
|
94
|
+
"feedback-collector-custom-validation": {
|
|
95
|
+
"type": "boolean"
|
|
93
96
|
}
|
|
94
97
|
}
|
|
95
98
|
}
|