@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.
Files changed (62) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/analytics.spec.yaml +29 -3
  3. package/dist/cjs/common/messages.js +14 -0
  4. package/dist/cjs/common/ui/error-boundary-ui/index.js +4 -3
  5. package/dist/cjs/common/ui/error-boundary-ui/messages.js +0 -5
  6. package/dist/cjs/common/utils/analytics/analytics.codegen.js +1 -1
  7. package/dist/cjs/common/utils/errors/index.js +39 -0
  8. package/dist/cjs/controllers/callback-context/main.js +23 -7
  9. package/dist/cjs/controllers/experience-tracker/index.js +85 -0
  10. package/dist/cjs/index.js +7 -0
  11. package/dist/cjs/ui/create-form/async-select/main.js +62 -20
  12. package/dist/cjs/ui/create-form/main.js +62 -7
  13. package/dist/cjs/ui/link-create/error-boundary/index.js +14 -9
  14. package/dist/cjs/ui/link-create/main.js +5 -3
  15. package/dist/cjs/ui/main.js +9 -9
  16. package/dist/es2019/common/messages.js +8 -0
  17. package/dist/es2019/common/ui/error-boundary-ui/index.js +2 -1
  18. package/dist/es2019/common/ui/error-boundary-ui/messages.js +0 -5
  19. package/dist/es2019/common/utils/analytics/analytics.codegen.js +1 -1
  20. package/dist/es2019/common/utils/errors/index.js +31 -0
  21. package/dist/es2019/controllers/callback-context/main.js +19 -3
  22. package/dist/es2019/controllers/experience-tracker/index.js +75 -0
  23. package/dist/es2019/index.js +1 -0
  24. package/dist/es2019/ui/create-form/async-select/main.js +32 -1
  25. package/dist/es2019/ui/create-form/main.js +47 -3
  26. package/dist/es2019/ui/link-create/error-boundary/index.js +14 -9
  27. package/dist/es2019/ui/link-create/main.js +5 -3
  28. package/dist/es2019/ui/main.js +10 -9
  29. package/dist/esm/common/messages.js +8 -0
  30. package/dist/esm/common/ui/error-boundary-ui/index.js +2 -1
  31. package/dist/esm/common/ui/error-boundary-ui/messages.js +0 -5
  32. package/dist/esm/common/utils/analytics/analytics.codegen.js +1 -1
  33. package/dist/esm/common/utils/errors/index.js +32 -0
  34. package/dist/esm/controllers/callback-context/main.js +23 -7
  35. package/dist/esm/controllers/experience-tracker/index.js +75 -0
  36. package/dist/esm/index.js +1 -0
  37. package/dist/esm/ui/create-form/async-select/main.js +62 -20
  38. package/dist/esm/ui/create-form/main.js +62 -7
  39. package/dist/esm/ui/link-create/error-boundary/index.js +14 -9
  40. package/dist/esm/ui/link-create/main.js +5 -3
  41. package/dist/esm/ui/main.js +10 -10
  42. package/dist/types/common/messages.d.ts +8 -0
  43. package/dist/types/common/ui/error-boundary-ui/messages.d.ts +0 -5
  44. package/dist/types/common/utils/analytics/analytics.codegen.d.ts +11 -2
  45. package/dist/types/common/utils/errors/index.d.ts +10 -0
  46. package/dist/types/controllers/callback-context/main.d.ts +1 -1
  47. package/dist/types/controllers/experience-tracker/index.d.ts +22 -0
  48. package/dist/types/index.d.ts +1 -0
  49. package/dist/types/ui/create-form/async-select/main.d.ts +1 -1
  50. package/dist/types/ui/create-form/main.d.ts +18 -1
  51. package/dist/types-ts4.5/common/messages.d.ts +8 -0
  52. package/dist/types-ts4.5/common/ui/error-boundary-ui/messages.d.ts +0 -5
  53. package/dist/types-ts4.5/common/utils/analytics/analytics.codegen.d.ts +11 -2
  54. package/dist/types-ts4.5/common/utils/errors/index.d.ts +10 -0
  55. package/dist/types-ts4.5/controllers/callback-context/main.d.ts +1 -1
  56. package/dist/types-ts4.5/controllers/experience-tracker/index.d.ts +22 -0
  57. package/dist/types-ts4.5/index.d.ts +1 -0
  58. package/dist/types-ts4.5/ui/create-form/async-select/main.d.ts +1 -1
  59. package/dist/types-ts4.5/ui/create-form/main.d.ts +18 -1
  60. package/package.json +8 -4
  61. package/report.api.md +11 -8
  62. 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.enable-sentry-client')) {
19
- // Capture exception to Sentry
20
- captureException(error, 'link-create');
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
- // Fire UFO failed experience
35
- // failUfoExperience(ufoExperience.mounted);
36
- }, [createAnalyticsEvent]);
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
- setFormErrorMessage(error.message);
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",
@@ -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
- ...props
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.10.0" || '',
26
- componentName: COMPONENT_NAME,
27
- source: COMPONENT_NAME
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, {
@@ -0,0 +1,8 @@
1
+ import { defineMessages } from 'react-intl-next';
2
+ export default defineMessages({
3
+ genericErrorMessage: {
4
+ id: 'link-create.unknown-error.heading',
5
+ defaultMessage: 'Something went wrong',
6
+ description: 'Message when an unknown error occurs'
7
+ }
8
+ });
@@ -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(messages.heading),
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::be704bbbca0e49c927d1268b9a0f1d6a>>
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 = 5;
45
+ _context.next = 6;
33
46
  break;
34
47
  }
35
- _context.next = 5;
48
+ _context.next = 6;
36
49
  return _onCreate(result);
37
- case 5:
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.name
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 2:
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
@@ -1,2 +1,3 @@
1
+ export { FORM_ERROR } from 'final-form';
1
2
  export { default, TextField, CreateForm, Select, AsyncSelect, CreateFormLoader, FormSpy } from './ui/index';
2
3
  export { useLinkCreateCallback, LinkCreateCallbackProvider } from './controllers/callback-context';
@@ -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
- loadOptions = _ref.loadOptions,
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 _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
94
+ var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
53
95
  var query,
54
96
  options,
55
- _args = arguments;
56
- return _regeneratorRuntime.wrap(function _callee$(_context) {
57
- while (1) switch (_context.prev = _context.next) {
97
+ _args2 = arguments;
98
+ return _regeneratorRuntime.wrap(function _callee2$(_context2) {
99
+ while (1) switch (_context2.prev = _context2.next) {
58
100
  case 0:
59
- query = _args.length > 0 && _args[0] !== undefined ? _args[0] : '';
101
+ query = _args2.length > 0 && _args2[0] !== undefined ? _args2[0] : '';
60
102
  if (loadOptions) {
61
- _context.next = 3;
103
+ _context2.next = 3;
62
104
  break;
63
105
  }
64
- return _context.abrupt("return");
106
+ return _context2.abrupt("return");
65
107
  case 3:
66
- _context.prev = 3;
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
- _context.next = 9;
118
+ _context2.next = 9;
77
119
  return loadOptions(query);
78
120
  case 9:
79
- options = _context.sent;
121
+ options = _context2.sent;
80
122
  if (current) {
81
123
  setDefaultOptions(options);
82
124
  setIsLoadingDefaultOptions(false);
83
125
  }
84
- _context.next = 16;
126
+ _context2.next = 16;
85
127
  break;
86
128
  case 13:
87
- _context.prev = 13;
88
- _context.t0 = _context["catch"](3);
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 _context.stop();
136
+ return _context2.stop();
95
137
  }
96
- }, _callee, null, [[3, 13]]);
138
+ }, _callee2, null, [[3, 13]]);
97
139
  }));
98
140
  return function fetch() {
99
- return _ref2.apply(this, arguments);
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 (_ref3) {
131
- var fieldId = _ref3.fieldId,
132
- fieldProps = _objectWithoutProperties(_ref3, _excluded2);
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 (_ref3) {
87
- var submitting = _ref3.submitting,
88
- formProps = _objectWithoutProperties(_ref3, _excluded);
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
- formErrorMessage: formErrorMessage,
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