@atlaskit/link-create 1.10.0 → 1.11.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 +33 -0
- package/analytics.spec.yaml +29 -3
- package/dist/cjs/common/messages.js +14 -0
- package/dist/cjs/common/ui/error-boundary-ui/index.js +4 -3
- package/dist/cjs/common/ui/error-boundary-ui/messages.js +0 -5
- package/dist/cjs/common/utils/analytics/analytics.codegen.js +1 -1
- package/dist/cjs/common/utils/errors/index.js +39 -0
- package/dist/cjs/controllers/callback-context/main.js +23 -7
- package/dist/cjs/controllers/experience-tracker/index.js +85 -0
- package/dist/cjs/index.js +7 -0
- package/dist/cjs/ui/create-form/async-select/main.js +62 -20
- package/dist/cjs/ui/create-form/main.js +62 -7
- package/dist/cjs/ui/link-create/error-boundary/index.js +14 -9
- package/dist/cjs/ui/link-create/main.js +5 -3
- package/dist/cjs/ui/main.js +9 -9
- package/dist/es2019/common/messages.js +8 -0
- package/dist/es2019/common/ui/error-boundary-ui/index.js +2 -1
- package/dist/es2019/common/ui/error-boundary-ui/messages.js +0 -5
- package/dist/es2019/common/utils/analytics/analytics.codegen.js +1 -1
- package/dist/es2019/common/utils/errors/index.js +31 -0
- package/dist/es2019/controllers/callback-context/main.js +19 -3
- package/dist/es2019/controllers/experience-tracker/index.js +75 -0
- package/dist/es2019/index.js +1 -0
- package/dist/es2019/ui/create-form/async-select/main.js +32 -1
- package/dist/es2019/ui/create-form/main.js +47 -3
- package/dist/es2019/ui/link-create/error-boundary/index.js +14 -9
- package/dist/es2019/ui/link-create/main.js +5 -3
- package/dist/es2019/ui/main.js +10 -9
- package/dist/esm/common/messages.js +8 -0
- package/dist/esm/common/ui/error-boundary-ui/index.js +2 -1
- package/dist/esm/common/ui/error-boundary-ui/messages.js +0 -5
- package/dist/esm/common/utils/analytics/analytics.codegen.js +1 -1
- package/dist/esm/common/utils/errors/index.js +32 -0
- package/dist/esm/controllers/callback-context/main.js +23 -7
- package/dist/esm/controllers/experience-tracker/index.js +75 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/ui/create-form/async-select/main.js +62 -20
- package/dist/esm/ui/create-form/main.js +62 -7
- package/dist/esm/ui/link-create/error-boundary/index.js +14 -9
- package/dist/esm/ui/link-create/main.js +5 -3
- package/dist/esm/ui/main.js +10 -10
- package/dist/types/common/messages.d.ts +8 -0
- package/dist/types/common/ui/error-boundary-ui/messages.d.ts +0 -5
- package/dist/types/common/utils/analytics/analytics.codegen.d.ts +11 -2
- package/dist/types/common/utils/errors/index.d.ts +10 -0
- package/dist/types/controllers/callback-context/main.d.ts +1 -1
- package/dist/types/controllers/experience-tracker/index.d.ts +22 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/ui/create-form/async-select/main.d.ts +1 -1
- package/dist/types/ui/create-form/main.d.ts +18 -1
- package/dist/types-ts4.5/common/messages.d.ts +8 -0
- package/dist/types-ts4.5/common/ui/error-boundary-ui/messages.d.ts +0 -5
- package/dist/types-ts4.5/common/utils/analytics/analytics.codegen.d.ts +11 -2
- package/dist/types-ts4.5/common/utils/errors/index.d.ts +10 -0
- package/dist/types-ts4.5/controllers/callback-context/main.d.ts +1 -1
- package/dist/types-ts4.5/controllers/experience-tracker/index.d.ts +22 -0
- package/dist/types-ts4.5/index.d.ts +1 -0
- package/dist/types-ts4.5/ui/create-form/async-select/main.d.ts +1 -1
- package/dist/types-ts4.5/ui/create-form/main.d.ts +18 -1
- package/package.json +8 -4
- package/report.api.md +11 -8
- package/tmp/api-report-tmp.d.ts +9 -8
|
@@ -5,6 +5,7 @@ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
|
5
5
|
import { ANALYTICS_CHANNEL } from '../../../common/constants';
|
|
6
6
|
import { ErrorBoundaryUI } from '../../../common/ui/error-boundary-ui';
|
|
7
7
|
import createEventPayload from '../../../common/utils/analytics/analytics.codegen';
|
|
8
|
+
import { useExperience } from '../../../controllers/experience-tracker';
|
|
8
9
|
import { BaseErrorBoundary } from './error-boundary-base';
|
|
9
10
|
export const ErrorBoundary = ({
|
|
10
11
|
children,
|
|
@@ -13,14 +14,17 @@ export const ErrorBoundary = ({
|
|
|
13
14
|
const {
|
|
14
15
|
createAnalyticsEvent
|
|
15
16
|
} = useAnalyticsEvents();
|
|
17
|
+
const experience = getBooleanFF('platform.linking-platform.link-create.better-observability') ?
|
|
18
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
19
|
+
useExperience() : null;
|
|
16
20
|
const handleError = useCallback((error, info) => {
|
|
17
21
|
var _window, _window$navigator, _window2, _window2$navigator, _info$componentStack;
|
|
18
|
-
if (getBooleanFF('platform.linking-platform.link-create.
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
if (!getBooleanFF('platform.linking-platform.link-create.better-observability')) {
|
|
23
|
+
if (getBooleanFF('platform.linking-platform.link-create.enable-sentry-client')) {
|
|
24
|
+
// Capture exception to Sentry
|
|
25
|
+
captureException(error, 'link-create');
|
|
26
|
+
}
|
|
21
27
|
}
|
|
22
|
-
|
|
23
|
-
// Fire Analytics event
|
|
24
28
|
createAnalyticsEvent(createEventPayload('operational.linkCreate.unhandledErrorCaught', getBooleanFF('platform.linking-platform.link-create.enable-sentry-client') ? {
|
|
25
29
|
browserInfo: ((_window = window) === null || _window === void 0 ? void 0 : (_window$navigator = _window.navigator) === null || _window$navigator === void 0 ? void 0 : _window$navigator.userAgent) || 'unknown',
|
|
26
30
|
error: error.name,
|
|
@@ -30,10 +34,11 @@ export const ErrorBoundary = ({
|
|
|
30
34
|
error: error.toString(),
|
|
31
35
|
componentStack: (_info$componentStack = info === null || info === void 0 ? void 0 : info.componentStack) !== null && _info$componentStack !== void 0 ? _info$componentStack : ''
|
|
32
36
|
})).fire(ANALYTICS_CHANNEL);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
if (getBooleanFF('platform.linking-platform.link-create.better-observability')) {
|
|
38
|
+
// Track experience as failed for SLO
|
|
39
|
+
experience === null || experience === void 0 ? void 0 : experience.failure(error);
|
|
40
|
+
}
|
|
41
|
+
}, [createAnalyticsEvent, experience]);
|
|
37
42
|
return /*#__PURE__*/React.createElement(BaseErrorBoundary, {
|
|
38
43
|
onError: handleError,
|
|
39
44
|
errorComponent: errorComponent !== null && errorComponent !== void 0 ? errorComponent : /*#__PURE__*/React.createElement(ErrorBoundaryUI, null)
|
|
@@ -55,7 +55,9 @@ const LinkCreateWithModal = ({
|
|
|
55
55
|
}, [onCreate, setFormErrorMessage]);
|
|
56
56
|
const handleFailure = useCallback(error => {
|
|
57
57
|
// Set the form error message
|
|
58
|
-
|
|
58
|
+
if (error instanceof Error) {
|
|
59
|
+
setFormErrorMessage(error.message);
|
|
60
|
+
}
|
|
59
61
|
onFailure && onFailure(error);
|
|
60
62
|
}, [onFailure, setFormErrorMessage]);
|
|
61
63
|
const {
|
|
@@ -73,8 +75,8 @@ const LinkCreateWithModal = ({
|
|
|
73
75
|
}, [onCancel, getShouldShowWarning, showExitWarning]);
|
|
74
76
|
const handleCloseExitWarning = useCallback(() => setShowExitWarning(false), []);
|
|
75
77
|
return jsx(LinkCreateCallbackProvider, {
|
|
76
|
-
onCreate: handleCreate,
|
|
77
|
-
onFailure: handleFailure,
|
|
78
|
+
onCreate: getBooleanFF('platform.linking-platform.link-create.better-observability') ? onCreate : handleCreate,
|
|
79
|
+
onFailure: getBooleanFF('platform.linking-platform.link-create.better-observability') ? onFailure : handleFailure,
|
|
78
80
|
onCancel: handleCancel
|
|
79
81
|
}, jsx(ModalTransition, null, active && jsx(Modal, {
|
|
80
82
|
testId: "link-create-modal",
|
package/dist/es2019/ui/main.js
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
/** @jsx jsx */
|
|
2
|
-
import { memo } from 'react';
|
|
2
|
+
import { Fragment, memo } from 'react';
|
|
3
3
|
import { jsx } from '@emotion/react';
|
|
4
4
|
import { AnalyticsContext } from '@atlaskit/analytics-next';
|
|
5
5
|
import { IntlMessagesProvider } from '@atlaskit/intl-messages-provider';
|
|
6
|
+
import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
6
7
|
import { COMPONENT_NAME } from '../common/constants';
|
|
7
8
|
import { ErrorBoundaryModal } from '../common/ui/error-boundary-modal';
|
|
8
9
|
import { withLinkCreateAnalyticsContext } from '../common/utils/analytics';
|
|
9
10
|
import { fetchMessagesForLocale } from '../common/utils/locale/fetch-messages-for-locale';
|
|
11
|
+
import { Experience } from '../controllers/experience-tracker';
|
|
10
12
|
import i18nEN from '../i18n/en';
|
|
11
13
|
import LinkCreate from './link-create';
|
|
12
14
|
import { ErrorBoundary } from './link-create/error-boundary';
|
|
13
|
-
const LinkCreateWithAnalyticsContext = withLinkCreateAnalyticsContext( /*#__PURE__*/memo(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return jsx(ErrorBoundary, {
|
|
15
|
+
const LinkCreateWithAnalyticsContext = withLinkCreateAnalyticsContext( /*#__PURE__*/memo(props => {
|
|
16
|
+
const ExperienceProvider = getBooleanFF('platform.linking-platform.link-create.better-observability') ? Experience : Fragment;
|
|
17
|
+
return jsx(ExperienceProvider, null, jsx(ErrorBoundary, {
|
|
17
18
|
errorComponent: jsx(ErrorBoundaryModal, {
|
|
18
19
|
active: props.active,
|
|
19
20
|
onClose: props.onCancel
|
|
20
21
|
})
|
|
21
|
-
}, jsx(LinkCreate, props));
|
|
22
|
+
}, jsx(LinkCreate, props)));
|
|
22
23
|
}));
|
|
23
24
|
export const PACKAGE_DATA = {
|
|
24
25
|
packageName: "@atlaskit/link-create" || '',
|
|
25
|
-
packageVersion: "1.
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
packageVersion: "1.11.0" || '',
|
|
27
|
+
component: COMPONENT_NAME,
|
|
28
|
+
componentName: COMPONENT_NAME
|
|
28
29
|
};
|
|
29
30
|
const ComposedLinkCreate = /*#__PURE__*/memo(props => {
|
|
30
31
|
return jsx(AnalyticsContext, {
|
|
@@ -3,6 +3,7 @@ import React from 'react';
|
|
|
3
3
|
import { FormattedMessage, useIntl } from 'react-intl-next';
|
|
4
4
|
import Button from '@atlaskit/button';
|
|
5
5
|
import EmptyState from '@atlaskit/empty-state';
|
|
6
|
+
import commonMessages from '../../messages';
|
|
6
7
|
import ErrorSVG from './error-svg';
|
|
7
8
|
import messages from './messages';
|
|
8
9
|
export var CONTACT_SUPPORT_LINK = 'https://support.atlassian.com/contact/';
|
|
@@ -11,7 +12,7 @@ export var ErrorBoundaryUI = function ErrorBoundaryUI() {
|
|
|
11
12
|
return /*#__PURE__*/React.createElement(EmptyState, {
|
|
12
13
|
maxImageWidth: 82,
|
|
13
14
|
testId: 'link-create-error-boundary-ui',
|
|
14
|
-
header: intl.formatMessage(
|
|
15
|
+
header: intl.formatMessage(commonMessages.genericErrorMessage),
|
|
15
16
|
description: /*#__PURE__*/React.createElement(FormattedMessage, _extends({}, messages.description, {
|
|
16
17
|
values: {
|
|
17
18
|
a: function a(label) {
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { defineMessages } from 'react-intl-next';
|
|
2
2
|
export default defineMessages({
|
|
3
|
-
heading: {
|
|
4
|
-
id: 'link-create.unknown-error.heading',
|
|
5
|
-
defaultMessage: 'Something went wrong',
|
|
6
|
-
description: 'Heading when an unknown error occurs'
|
|
7
|
-
},
|
|
8
3
|
description: {
|
|
9
4
|
id: 'link-create.unknown-error.description',
|
|
10
5
|
defaultMessage: 'Refresh the page, or contact <a>Atlassian Support</a> if this keeps happening.',
|
|
@@ -4,7 +4,7 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
|
4
4
|
*
|
|
5
5
|
* Generates Typescript types for analytics events from analytics.spec.yaml
|
|
6
6
|
*
|
|
7
|
-
* @codegen <<SignedSource::
|
|
7
|
+
* @codegen <<SignedSource::20562f2db603daab7d5b7a5c5ba1c04b>>
|
|
8
8
|
* @codegenCommand yarn workspace @atlaskit/link-create run codegen-analytics
|
|
9
9
|
*/
|
|
10
10
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import _typeof from "@babel/runtime/helpers/typeof";
|
|
2
|
+
import { getTraceId } from '@atlaskit/linking-common/utils';
|
|
3
|
+
var getUrlPath = function getUrlPath(url) {
|
|
4
|
+
try {
|
|
5
|
+
return new URL(url).pathname;
|
|
6
|
+
} catch (_unused) {
|
|
7
|
+
return 'Failed to parse pathname from url';
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
export var getNetworkFields = function getNetworkFields(error) {
|
|
11
|
+
if (error instanceof Response) {
|
|
12
|
+
return {
|
|
13
|
+
traceId: getTraceId(error),
|
|
14
|
+
status: error.status,
|
|
15
|
+
path: getUrlPath(error.url)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
traceId: null,
|
|
20
|
+
status: null,
|
|
21
|
+
path: null
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export var getErrorType = function getErrorType(error) {
|
|
25
|
+
if (error instanceof Response) {
|
|
26
|
+
return 'NetworkError';
|
|
27
|
+
}
|
|
28
|
+
if (error instanceof Error) {
|
|
29
|
+
return error.name;
|
|
30
|
+
}
|
|
31
|
+
return _typeof(error);
|
|
32
|
+
};
|
|
@@ -5,8 +5,11 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
5
5
|
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
6
6
|
import React, { useContext, useMemo } from 'react';
|
|
7
7
|
import { useAnalyticsEvents } from '@atlaskit/analytics-next';
|
|
8
|
+
import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
8
9
|
import { ANALYTICS_CHANNEL } from '../../common/constants';
|
|
9
10
|
import createEventPayload from '../../common/utils/analytics/analytics.codegen';
|
|
11
|
+
import { getErrorType } from '../../common/utils/errors';
|
|
12
|
+
import { useExperience } from '../experience-tracker';
|
|
10
13
|
var LinkCreateCallbackContext = /*#__PURE__*/React.createContext({});
|
|
11
14
|
var LinkCreateCallbackProvider = function LinkCreateCallbackProvider(_ref) {
|
|
12
15
|
var children = _ref.children,
|
|
@@ -15,6 +18,9 @@ var LinkCreateCallbackProvider = function LinkCreateCallbackProvider(_ref) {
|
|
|
15
18
|
onCancel = _ref.onCancel;
|
|
16
19
|
var _useAnalyticsEvents = useAnalyticsEvents(),
|
|
17
20
|
createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
|
|
21
|
+
var experience = getBooleanFF('platform.linking-platform.link-create.better-observability') ?
|
|
22
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
23
|
+
useExperience() : null;
|
|
18
24
|
var handleCreate = useMemo(function () {
|
|
19
25
|
return {
|
|
20
26
|
onCreate: function () {
|
|
@@ -23,18 +29,25 @@ var LinkCreateCallbackProvider = function LinkCreateCallbackProvider(_ref) {
|
|
|
23
29
|
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
24
30
|
while (1) switch (_context.prev = _context.next) {
|
|
25
31
|
case 0:
|
|
32
|
+
if (getBooleanFF('platform.linking-platform.link-create.better-observability')) {
|
|
33
|
+
/**
|
|
34
|
+
* We consider the experience successful once we have
|
|
35
|
+
* successfully created an object
|
|
36
|
+
*/
|
|
37
|
+
experience === null || experience === void 0 || experience.success();
|
|
38
|
+
}
|
|
26
39
|
objectId = result.objectId, objectType = result.objectType;
|
|
27
40
|
createAnalyticsEvent(createEventPayload('track.object.created.linkCreate', {
|
|
28
41
|
objectId: objectId,
|
|
29
42
|
objectType: objectType
|
|
30
43
|
})).fire(ANALYTICS_CHANNEL);
|
|
31
44
|
if (!_onCreate) {
|
|
32
|
-
_context.next =
|
|
45
|
+
_context.next = 6;
|
|
33
46
|
break;
|
|
34
47
|
}
|
|
35
|
-
_context.next =
|
|
48
|
+
_context.next = 6;
|
|
36
49
|
return _onCreate(result);
|
|
37
|
-
case
|
|
50
|
+
case 6:
|
|
38
51
|
case "end":
|
|
39
52
|
return _context.stop();
|
|
40
53
|
}
|
|
@@ -46,7 +59,7 @@ var LinkCreateCallbackProvider = function LinkCreateCallbackProvider(_ref) {
|
|
|
46
59
|
return onCreate;
|
|
47
60
|
}()
|
|
48
61
|
};
|
|
49
|
-
}, [createAnalyticsEvent, _onCreate]);
|
|
62
|
+
}, [createAnalyticsEvent, _onCreate, experience]);
|
|
50
63
|
var handleFailure = useMemo(function () {
|
|
51
64
|
return {
|
|
52
65
|
onFailure: function () {
|
|
@@ -55,10 +68,13 @@ var LinkCreateCallbackProvider = function LinkCreateCallbackProvider(_ref) {
|
|
|
55
68
|
while (1) switch (_context2.prev = _context2.next) {
|
|
56
69
|
case 0:
|
|
57
70
|
createAnalyticsEvent(createEventPayload('track.object.createFailed.linkCreate', {
|
|
58
|
-
failureType: error
|
|
71
|
+
failureType: getErrorType(error)
|
|
59
72
|
})).fire(ANALYTICS_CHANNEL);
|
|
73
|
+
if (getBooleanFF('platform.linking-platform.link-create.better-observability')) {
|
|
74
|
+
experience === null || experience === void 0 || experience.failure(error);
|
|
75
|
+
}
|
|
60
76
|
_onFailure && _onFailure(error);
|
|
61
|
-
case
|
|
77
|
+
case 3:
|
|
62
78
|
case "end":
|
|
63
79
|
return _context2.stop();
|
|
64
80
|
}
|
|
@@ -70,7 +86,7 @@ var LinkCreateCallbackProvider = function LinkCreateCallbackProvider(_ref) {
|
|
|
70
86
|
return onFailure;
|
|
71
87
|
}()
|
|
72
88
|
};
|
|
73
|
-
}, [createAnalyticsEvent, _onFailure]);
|
|
89
|
+
}, [createAnalyticsEvent, _onFailure, experience]);
|
|
74
90
|
var value = useMemo(function () {
|
|
75
91
|
return _objectSpread(_objectSpread({
|
|
76
92
|
onCancel: onCancel
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
+
import React, { createContext, useContext, useMemo, useRef } from 'react';
|
|
5
|
+
import { useAnalyticsEvents } from '@atlaskit/analytics-next';
|
|
6
|
+
import { captureException } from '@atlaskit/linking-common/sentry';
|
|
7
|
+
import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
8
|
+
import { ANALYTICS_CHANNEL } from '../../common/constants';
|
|
9
|
+
import createEventPayload from '../../common/utils/analytics/analytics.codegen';
|
|
10
|
+
import { getErrorType, getNetworkFields } from '../../common/utils/errors';
|
|
11
|
+
var ExperienceContext = /*#__PURE__*/createContext({
|
|
12
|
+
success: function success() {},
|
|
13
|
+
failure: function failure() {}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Experience provider that simply keeps track of the state of the experience.
|
|
18
|
+
* Fires an operational event when experience state changes to FAILED.
|
|
19
|
+
*/
|
|
20
|
+
export var Experience = function Experience(_ref) {
|
|
21
|
+
var children = _ref.children;
|
|
22
|
+
var _useAnalyticsEvents = useAnalyticsEvents(),
|
|
23
|
+
createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
|
|
24
|
+
var experience = useRef('STARTED');
|
|
25
|
+
var value = useMemo(function () {
|
|
26
|
+
return {
|
|
27
|
+
success: function success() {
|
|
28
|
+
if (experience.current !== 'SUCCEEDED') {
|
|
29
|
+
experience.current = 'SUCCEEDED';
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* Indicate the experience has failed and capture exception information
|
|
34
|
+
* @param error Typically an Error class or Response class
|
|
35
|
+
*/
|
|
36
|
+
failure: function failure(error) {
|
|
37
|
+
var experienceStatus = 'FAILED';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Always capture an event to Splunk
|
|
41
|
+
*/
|
|
42
|
+
createAnalyticsEvent(createEventPayload('operational.operation.failed.linkCreate', _objectSpread({
|
|
43
|
+
/**
|
|
44
|
+
* The type of error that has failed the experience
|
|
45
|
+
*/
|
|
46
|
+
errorType: getErrorType(error),
|
|
47
|
+
/**
|
|
48
|
+
* The current status of the experience (has failed)
|
|
49
|
+
*/
|
|
50
|
+
experienceStatus: experienceStatus,
|
|
51
|
+
/**
|
|
52
|
+
* Previous experience status indicates whether the experience
|
|
53
|
+
* has just failed now, or has already failing
|
|
54
|
+
*/
|
|
55
|
+
previousExperienceStatus: experience.current
|
|
56
|
+
}, getNetworkFields(error)))).fire(ANALYTICS_CHANNEL);
|
|
57
|
+
if (error instanceof Error) {
|
|
58
|
+
if (getBooleanFF('platform.linking-platform.link-create.enable-sentry-client')) {
|
|
59
|
+
// Capture exception to Sentry
|
|
60
|
+
captureException(error, 'link-create');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (experience.current !== experienceStatus) {
|
|
64
|
+
experience.current = experienceStatus;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}, [experience, createAnalyticsEvent]);
|
|
69
|
+
return /*#__PURE__*/React.createElement(ExperienceContext.Provider, {
|
|
70
|
+
value: value
|
|
71
|
+
}, children);
|
|
72
|
+
};
|
|
73
|
+
export var useExperience = function useExperience() {
|
|
74
|
+
return useContext(ExperienceContext);
|
|
75
|
+
};
|
package/dist/esm/index.js
CHANGED
|
@@ -10,8 +10,13 @@ import { useEffect, useMemo, useState } from 'react';
|
|
|
10
10
|
import { jsx } from '@emotion/react';
|
|
11
11
|
import debounce from 'debounce-promise';
|
|
12
12
|
import { useForm } from 'react-final-form';
|
|
13
|
+
import { useIntl } from 'react-intl-next';
|
|
14
|
+
import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
13
15
|
import { AsyncSelect as AkAsyncSelect } from '@atlaskit/select';
|
|
16
|
+
import messages from '../../../common/messages';
|
|
17
|
+
import { useLinkCreateCallback } from '../../../controllers/callback-context';
|
|
14
18
|
import { CreateField } from '../../../controllers/create-field';
|
|
19
|
+
import { useFormContext } from '../../../controllers/form-context';
|
|
15
20
|
export var TEST_ID = 'link-create-async-select';
|
|
16
21
|
|
|
17
22
|
/**
|
|
@@ -30,10 +35,15 @@ export function AsyncSelect(_ref) {
|
|
|
30
35
|
_ref$testId = _ref.testId,
|
|
31
36
|
testId = _ref$testId === void 0 ? TEST_ID : _ref$testId,
|
|
32
37
|
propsDefaultValue = _ref.defaultOption,
|
|
33
|
-
|
|
38
|
+
loadOptionsFn = _ref.loadOptions,
|
|
34
39
|
restProps = _objectWithoutProperties(_ref, _excluded);
|
|
35
40
|
var _useForm = useForm(),
|
|
36
41
|
mutators = _useForm.mutators;
|
|
42
|
+
var _useLinkCreateCallbac = useLinkCreateCallback(),
|
|
43
|
+
onFailure = _useLinkCreateCallbac.onFailure;
|
|
44
|
+
var _useFormContext = useFormContext(),
|
|
45
|
+
setFormErrorMessage = _useFormContext.setFormErrorMessage;
|
|
46
|
+
var intl = useIntl();
|
|
37
47
|
var _useState = useState(propsDefaultValue),
|
|
38
48
|
_useState2 = _slicedToArray(_useState, 2),
|
|
39
49
|
defaultValue = _useState2[0],
|
|
@@ -46,24 +56,56 @@ export function AsyncSelect(_ref) {
|
|
|
46
56
|
_useState6 = _slicedToArray(_useState5, 2),
|
|
47
57
|
defaultOptions = _useState6[0],
|
|
48
58
|
setDefaultOptions = _useState6[1];
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* This binds experience to fail if async fetch ever fails to load
|
|
62
|
+
*/
|
|
63
|
+
var loadOptions = getBooleanFF('platform.linking-platform.link-create.better-observability') ?
|
|
64
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
65
|
+
useMemo(function () {
|
|
66
|
+
if (loadOptionsFn) {
|
|
67
|
+
return /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
|
|
68
|
+
var _args = arguments;
|
|
69
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
70
|
+
while (1) switch (_context.prev = _context.next) {
|
|
71
|
+
case 0:
|
|
72
|
+
_context.prev = 0;
|
|
73
|
+
_context.next = 3;
|
|
74
|
+
return loadOptionsFn.apply(void 0, _args);
|
|
75
|
+
case 3:
|
|
76
|
+
return _context.abrupt("return", _context.sent);
|
|
77
|
+
case 6:
|
|
78
|
+
_context.prev = 6;
|
|
79
|
+
_context.t0 = _context["catch"](0);
|
|
80
|
+
onFailure === null || onFailure === void 0 || onFailure(_context.t0);
|
|
81
|
+
setFormErrorMessage(intl.formatMessage(messages.genericErrorMessage));
|
|
82
|
+
return _context.abrupt("return", []);
|
|
83
|
+
case 11:
|
|
84
|
+
case "end":
|
|
85
|
+
return _context.stop();
|
|
86
|
+
}
|
|
87
|
+
}, _callee, null, [[0, 6]]);
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
}, [intl, onFailure, loadOptionsFn, setFormErrorMessage]) : loadOptionsFn;
|
|
49
91
|
useEffect(function () {
|
|
50
92
|
var current = true;
|
|
51
93
|
var fetch = /*#__PURE__*/function () {
|
|
52
|
-
var
|
|
94
|
+
var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
|
|
53
95
|
var query,
|
|
54
96
|
options,
|
|
55
|
-
|
|
56
|
-
return _regeneratorRuntime.wrap(function
|
|
57
|
-
while (1) switch (
|
|
97
|
+
_args2 = arguments;
|
|
98
|
+
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
|
|
99
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
58
100
|
case 0:
|
|
59
|
-
query =
|
|
101
|
+
query = _args2.length > 0 && _args2[0] !== undefined ? _args2[0] : '';
|
|
60
102
|
if (loadOptions) {
|
|
61
|
-
|
|
103
|
+
_context2.next = 3;
|
|
62
104
|
break;
|
|
63
105
|
}
|
|
64
|
-
return
|
|
106
|
+
return _context2.abrupt("return");
|
|
65
107
|
case 3:
|
|
66
|
-
|
|
108
|
+
_context2.prev = 3;
|
|
67
109
|
/**
|
|
68
110
|
* If we are fetching default options, clear the
|
|
69
111
|
* value the user has set
|
|
@@ -73,30 +115,30 @@ export function AsyncSelect(_ref) {
|
|
|
73
115
|
}
|
|
74
116
|
setIsLoadingDefaultOptions(true);
|
|
75
117
|
setDefaultOptions([]);
|
|
76
|
-
|
|
118
|
+
_context2.next = 9;
|
|
77
119
|
return loadOptions(query);
|
|
78
120
|
case 9:
|
|
79
|
-
options =
|
|
121
|
+
options = _context2.sent;
|
|
80
122
|
if (current) {
|
|
81
123
|
setDefaultOptions(options);
|
|
82
124
|
setIsLoadingDefaultOptions(false);
|
|
83
125
|
}
|
|
84
|
-
|
|
126
|
+
_context2.next = 16;
|
|
85
127
|
break;
|
|
86
128
|
case 13:
|
|
87
|
-
|
|
88
|
-
|
|
129
|
+
_context2.prev = 13;
|
|
130
|
+
_context2.t0 = _context2["catch"](3);
|
|
89
131
|
if (current) {
|
|
90
132
|
setIsLoadingDefaultOptions(false);
|
|
91
133
|
}
|
|
92
134
|
case 16:
|
|
93
135
|
case "end":
|
|
94
|
-
return
|
|
136
|
+
return _context2.stop();
|
|
95
137
|
}
|
|
96
|
-
},
|
|
138
|
+
}, _callee2, null, [[3, 13]]);
|
|
97
139
|
}));
|
|
98
140
|
return function fetch() {
|
|
99
|
-
return
|
|
141
|
+
return _ref3.apply(this, arguments);
|
|
100
142
|
};
|
|
101
143
|
}();
|
|
102
144
|
fetch();
|
|
@@ -127,9 +169,9 @@ export function AsyncSelect(_ref) {
|
|
|
127
169
|
validators: validators,
|
|
128
170
|
validationHelpText: validationHelpText,
|
|
129
171
|
testId: testId
|
|
130
|
-
}, function (
|
|
131
|
-
var fieldId =
|
|
132
|
-
fieldProps = _objectWithoutProperties(
|
|
172
|
+
}, function (_ref4) {
|
|
173
|
+
var fieldId = _ref4.fieldId,
|
|
174
|
+
fieldProps = _objectWithoutProperties(_ref4, _excluded2);
|
|
133
175
|
return jsx(AkAsyncSelect, _extends({
|
|
134
176
|
inputId: fieldId
|
|
135
177
|
}, fieldProps, restProps, {
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import _typeof from "@babel/runtime/helpers/typeof";
|
|
2
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
3
|
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
3
4
|
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
4
|
-
var _excluded = ["submitting"];
|
|
5
|
+
var _excluded = ["submitting", "submitError"];
|
|
5
6
|
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
6
7
|
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
|
|
7
8
|
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
8
9
|
/** @jsx jsx */
|
|
9
10
|
import { useCallback } from 'react';
|
|
10
11
|
import { css, jsx } from '@emotion/react';
|
|
12
|
+
import { FORM_ERROR } from 'final-form';
|
|
11
13
|
import { Form, FormSpy } from 'react-final-form';
|
|
14
|
+
import { useIntl } from 'react-intl-next';
|
|
12
15
|
import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
13
16
|
import { Box } from '@atlaskit/primitives';
|
|
14
17
|
import { CREATE_FORM_MAX_WIDTH_IN_PX, LINK_CREATE_FORM_POST_CREATE_FIELD } from '../../common/constants';
|
|
18
|
+
import messages from '../../common/messages';
|
|
19
|
+
import { useLinkCreateCallback } from '../../controllers/callback-context';
|
|
15
20
|
import { useExitWarningModal } from '../../controllers/exit-warning-modal-context';
|
|
16
21
|
import { useFormContext } from '../../controllers/form-context';
|
|
17
22
|
import { CreateFormFooter } from './form-footer';
|
|
@@ -33,10 +38,14 @@ export var CreateForm = function CreateForm(_ref) {
|
|
|
33
38
|
hideFooter = _ref.hideFooter,
|
|
34
39
|
initialValues = _ref.initialValues;
|
|
35
40
|
var _useFormContext = useFormContext(),
|
|
41
|
+
setFormErrorMessage = _useFormContext.setFormErrorMessage,
|
|
36
42
|
formErrorMessage = _useFormContext.formErrorMessage,
|
|
37
43
|
enableEditView = _useFormContext.enableEditView;
|
|
44
|
+
var intl = useIntl();
|
|
38
45
|
var _useExitWarningModal = useExitWarningModal(),
|
|
39
46
|
setShouldShowWarning = _useExitWarningModal.setShouldShowWarning;
|
|
47
|
+
var _useLinkCreateCallbac = useLinkCreateCallback(),
|
|
48
|
+
onFailure = _useLinkCreateCallbac.onFailure;
|
|
40
49
|
var handleSubmit = useCallback( /*#__PURE__*/function () {
|
|
41
50
|
var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(data) {
|
|
42
51
|
var shouldEnableEditView, formData;
|
|
@@ -54,6 +63,11 @@ export var CreateForm = function CreateForm(_ref) {
|
|
|
54
63
|
* if submission is successful
|
|
55
64
|
*/
|
|
56
65
|
enableEditView === null || enableEditView === void 0 || enableEditView(!!shouldEnableEditView);
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* This is the onSubmit handler provided by the plugin
|
|
69
|
+
* It will be async, and it will likely involve awaiting `onCreate` (the adopters handler)
|
|
70
|
+
*/
|
|
57
71
|
return _context.abrupt("return", onSubmit(formData));
|
|
58
72
|
case 4:
|
|
59
73
|
return _context.abrupt("return", onSubmit(data));
|
|
@@ -67,6 +81,40 @@ export var CreateForm = function CreateForm(_ref) {
|
|
|
67
81
|
return _ref2.apply(this, arguments);
|
|
68
82
|
};
|
|
69
83
|
}(), [onSubmit, enableEditView]);
|
|
84
|
+
var handleSubmitWithErrorHandling = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
|
|
85
|
+
var _args2 = arguments;
|
|
86
|
+
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
|
|
87
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
88
|
+
case 0:
|
|
89
|
+
_context2.prev = 0;
|
|
90
|
+
/**
|
|
91
|
+
* Clear any error message that may have been set by async select fields
|
|
92
|
+
* This will immediately remove any indication of an error, but the form likely will fail to submit,
|
|
93
|
+
* it will be likely a 400 because the user probably could not set all fields anyway
|
|
94
|
+
*/
|
|
95
|
+
setFormErrorMessage();
|
|
96
|
+
_context2.next = 4;
|
|
97
|
+
return handleSubmit.apply(void 0, _args2);
|
|
98
|
+
case 4:
|
|
99
|
+
return _context2.abrupt("return", _context2.sent);
|
|
100
|
+
case 7:
|
|
101
|
+
_context2.prev = 7;
|
|
102
|
+
_context2.t0 = _context2["catch"](0);
|
|
103
|
+
/**
|
|
104
|
+
* Notify link create of failed experience
|
|
105
|
+
*/
|
|
106
|
+
onFailure === null || onFailure === void 0 || onFailure(_context2.t0);
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Return a generic message for react final form to render
|
|
110
|
+
*/
|
|
111
|
+
return _context2.abrupt("return", _defineProperty({}, FORM_ERROR, intl.formatMessage(messages.genericErrorMessage)));
|
|
112
|
+
case 11:
|
|
113
|
+
case "end":
|
|
114
|
+
return _context2.stop();
|
|
115
|
+
}
|
|
116
|
+
}, _callee2, null, [[0, 7]]);
|
|
117
|
+
})), [handleSubmit, setFormErrorMessage, intl, onFailure]);
|
|
70
118
|
var handleCancel = useCallback(function () {
|
|
71
119
|
onCancel && onCancel();
|
|
72
120
|
}, [onCancel]);
|
|
@@ -74,7 +122,7 @@ export var CreateForm = function CreateForm(_ref) {
|
|
|
74
122
|
return jsx(CreateFormLoader, null);
|
|
75
123
|
}
|
|
76
124
|
return jsx(Form, {
|
|
77
|
-
onSubmit: handleSubmit,
|
|
125
|
+
onSubmit: getBooleanFF('platform.linking-platform.link-create.better-observability') ? handleSubmitWithErrorHandling : handleSubmit,
|
|
78
126
|
initialValues: initialValues,
|
|
79
127
|
mutators: {
|
|
80
128
|
setField: function setField(args, state, tools) {
|
|
@@ -83,9 +131,10 @@ export var CreateForm = function CreateForm(_ref) {
|
|
|
83
131
|
});
|
|
84
132
|
}
|
|
85
133
|
}
|
|
86
|
-
}, function (
|
|
87
|
-
var submitting =
|
|
88
|
-
|
|
134
|
+
}, function (_ref5) {
|
|
135
|
+
var submitting = _ref5.submitting,
|
|
136
|
+
submitError = _ref5.submitError,
|
|
137
|
+
formProps = _objectWithoutProperties(_ref5, _excluded);
|
|
89
138
|
return jsx("form", {
|
|
90
139
|
onSubmit: formProps.handleSubmit,
|
|
91
140
|
name: "link-create-form",
|
|
@@ -106,8 +155,14 @@ export var CreateForm = function CreateForm(_ref) {
|
|
|
106
155
|
});
|
|
107
156
|
setShouldShowWarning(isModified);
|
|
108
157
|
}
|
|
109
|
-
}), jsx(Box, null, children), !hideFooter && jsx(CreateFormFooter
|
|
110
|
-
|
|
158
|
+
}), jsx(Box, null, children), !hideFooter && jsx(CreateFormFooter
|
|
159
|
+
/**
|
|
160
|
+
* We will prefer to render the error message connected to
|
|
161
|
+
* react final form state (submitError) otherwise we can
|
|
162
|
+
* default to the `formErrorMessage` that we sometimes use with our own
|
|
163
|
+
* "form context" (only currently used for AsyncSelect field reporting failed loading)
|
|
164
|
+
*/, {
|
|
165
|
+
formErrorMessage: getBooleanFF('platform.linking-platform.link-create.better-observability') ? submitError || formErrorMessage : formErrorMessage,
|
|
111
166
|
handleCancel: handleCancel,
|
|
112
167
|
submitting: submitting,
|
|
113
168
|
testId: testId
|