@atlaskit/link-picker 1.6.1 → 1.8.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 (57) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cjs/analytics.codegen.js +28 -0
  3. package/dist/cjs/common/constants.js +10 -0
  4. package/dist/cjs/{ui/error-boundary-fallback → common}/generic-error-svg/index.js +0 -0
  5. package/dist/cjs/{ui/error-boundary-fallback → common}/generic-error-svg/styled.js +0 -0
  6. package/dist/cjs/services/use-plugins/index.js +32 -35
  7. package/dist/cjs/services/use-search-query/index.js +41 -0
  8. package/dist/cjs/ui/error-boundary-fallback/index.js +1 -1
  9. package/dist/cjs/ui/index.js +4 -4
  10. package/dist/cjs/ui/link-picker/index.js +74 -46
  11. package/dist/cjs/ui/link-picker/link-search-error/index.js +27 -0
  12. package/dist/cjs/ui/link-picker/messages.js +10 -0
  13. package/dist/cjs/ui/link-picker/text-input/index.js +4 -10
  14. package/dist/cjs/ui/link-picker/url.js +0 -5
  15. package/dist/cjs/version.json +1 -1
  16. package/dist/es2019/analytics.codegen.js +20 -0
  17. package/dist/es2019/common/constants.js +2 -0
  18. package/dist/es2019/{ui/error-boundary-fallback → common}/generic-error-svg/index.js +0 -0
  19. package/dist/es2019/{ui/error-boundary-fallback → common}/generic-error-svg/styled.js +0 -0
  20. package/dist/es2019/services/use-plugins/index.js +28 -38
  21. package/dist/es2019/services/use-search-query/index.js +32 -0
  22. package/dist/es2019/ui/error-boundary-fallback/index.js +1 -1
  23. package/dist/es2019/ui/index.js +1 -2
  24. package/dist/es2019/ui/link-picker/index.js +65 -45
  25. package/dist/es2019/ui/link-picker/link-search-error/index.js +10 -0
  26. package/dist/es2019/ui/link-picker/messages.js +10 -0
  27. package/dist/es2019/ui/link-picker/text-input/index.js +3 -9
  28. package/dist/es2019/ui/link-picker/url.js +0 -5
  29. package/dist/es2019/version.json +1 -1
  30. package/dist/esm/analytics.codegen.js +20 -0
  31. package/dist/esm/common/constants.js +2 -0
  32. package/dist/esm/{ui/error-boundary-fallback → common}/generic-error-svg/index.js +0 -0
  33. package/dist/esm/{ui/error-boundary-fallback → common}/generic-error-svg/styled.js +0 -0
  34. package/dist/esm/services/use-plugins/index.js +33 -35
  35. package/dist/esm/services/use-search-query/index.js +32 -0
  36. package/dist/esm/ui/error-boundary-fallback/index.js +1 -1
  37. package/dist/esm/ui/index.js +1 -2
  38. package/dist/esm/ui/link-picker/index.js +67 -45
  39. package/dist/esm/ui/link-picker/link-search-error/index.js +14 -0
  40. package/dist/esm/ui/link-picker/messages.js +10 -0
  41. package/dist/esm/ui/link-picker/text-input/index.js +4 -10
  42. package/dist/esm/ui/link-picker/url.js +0 -5
  43. package/dist/esm/version.json +1 -1
  44. package/dist/types/analytics.codegen.d.ts +20 -0
  45. package/dist/types/common/constants.d.ts +2 -0
  46. package/dist/types/{ui/error-boundary-fallback → common}/generic-error-svg/index.d.ts +0 -0
  47. package/dist/types/{ui/error-boundary-fallback → common}/generic-error-svg/styled.d.ts +0 -0
  48. package/dist/types/index.d.ts +1 -1
  49. package/dist/types/services/use-plugins/index.d.ts +6 -5
  50. package/dist/types/services/use-search-query/index.d.ts +3 -0
  51. package/dist/types/ui/link-picker/index.d.ts +2 -0
  52. package/dist/types/ui/link-picker/link-search-error/index.d.ts +4 -0
  53. package/dist/types/ui/link-picker/messages.d.ts +10 -0
  54. package/dist/types/ui/link-picker/text-input/index.d.ts +0 -1
  55. package/dist/types/ui/types.d.ts +5 -2
  56. package/package.json +11 -5
  57. package/report.api.md +11 -5
@@ -7,64 +7,53 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
7
7
 
8
8
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
9
9
 
10
- import { useEffect, useRef, useReducer, useMemo } from 'react';
10
+ import { useEffect, useReducer, useMemo, useRef, useState, useCallback } from 'react';
11
11
  import { RECENT_SEARCH_LIST_SIZE } from '../../ui/link-picker';
12
- import { isSafeUrl } from '../../ui/link-picker/url';
13
12
 
14
13
  function reducer(state, payload) {
15
14
  return _objectSpread(_objectSpread({}, state), payload);
16
15
  }
17
16
 
18
- export function usePlugins(state, plugins) {
17
+ export function usePlugins(state, activeTab, plugins) {
18
+ var _useState = useState(0),
19
+ _useState2 = _slicedToArray(_useState, 2),
20
+ retries = _useState2[0],
21
+ setRetries = _useState2[1];
22
+
19
23
  var _useReducer = useReducer(reducer, {
20
- items: [],
24
+ items: null,
25
+ error: null,
21
26
  isLoading: false
22
27
  }),
23
28
  _useReducer2 = _slicedToArray(_useReducer, 2),
24
29
  pluginState = _useReducer2[0],
25
30
  dispatch = _useReducer2[1];
26
31
 
27
- var url = state.url,
28
- selectedIndex = state.selectedIndex;
29
32
  var items = pluginState.items,
30
- isLoading = pluginState.isLoading;
31
- var activePlugin = !plugins || plugins.length === 0 ? null : plugins[state.activeTab];
33
+ isLoading = pluginState.isLoading,
34
+ error = pluginState.error;
32
35
  var queryVersion = useRef(0);
33
- var pluginRef = useRef(activePlugin);
34
- var selectedItem = items[selectedIndex];
35
- var isSelectedItem = (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.url) === url;
36
+ var activePlugin = plugins === null || plugins === void 0 ? void 0 : plugins[activeTab];
36
37
  useEffect(function () {
37
38
  if (!activePlugin) {
38
39
  return;
39
40
  }
40
41
 
41
- if (isSelectedItem) {
42
- return;
43
- }
44
-
45
- if (isSafeUrl(url) && url.length) {
42
+ if (!state) {
46
43
  dispatch({
47
- items: [],
48
- isLoading: false
44
+ items: null,
45
+ isLoading: false,
46
+ error: null
49
47
  });
50
48
  return;
51
49
  }
52
50
 
53
- var updates = activePlugin.resolve({
54
- query: url
51
+ var updates = activePlugin.resolve(state);
52
+ dispatch({
53
+ items: null,
54
+ isLoading: true,
55
+ error: null
55
56
  });
56
-
57
- if (pluginRef.current !== activePlugin) {
58
- dispatch({
59
- items: [],
60
- isLoading: true
61
- });
62
- } else {
63
- dispatch({
64
- isLoading: true
65
- });
66
- }
67
-
68
57
  var newQuery = ++queryVersion.current;
69
58
 
70
59
  var updateResults = /*#__PURE__*/function () {
@@ -142,6 +131,8 @@ export function usePlugins(state, plugins) {
142
131
  _context.prev = 25;
143
132
  _context.t0 = _context["catch"](0);
144
133
  dispatch({
134
+ error: _context.t0,
135
+ items: null,
145
136
  isLoading: false
146
137
  });
147
138
 
@@ -159,7 +150,7 @@ export function usePlugins(state, plugins) {
159
150
  }();
160
151
 
161
152
  updateResults();
162
- }, [activePlugin, queryVersion, url, isSelectedItem]);
153
+ }, [activePlugin, queryVersion, state, retries]);
163
154
  var tabs = useMemo(function () {
164
155
  if (!plugins || plugins.length <= 1) {
165
156
  return [];
@@ -173,12 +164,19 @@ export function usePlugins(state, plugins) {
173
164
  };
174
165
  });
175
166
  }, [plugins]);
167
+ var handleRetry = useCallback(function () {
168
+ setRetries(function (prev) {
169
+ return ++prev;
170
+ });
171
+ }, []);
176
172
  return {
177
173
  tabs: tabs,
178
174
  items: items,
179
175
  isLoading: isLoading,
180
- isSelectedItem: isSelectedItem,
181
- isActivePlugin: !!activePlugin
176
+ isActivePlugin: !!activePlugin,
177
+ error: error,
178
+ retry: handleRetry,
179
+ errorFallback: activePlugin === null || activePlugin === void 0 ? void 0 : activePlugin.errorFallback
182
180
  };
183
181
  }
184
182
 
@@ -0,0 +1,32 @@
1
+ import { useRef } from 'react';
2
+ import { isSafeUrl } from '../../ui/link-picker/url';
3
+ export function useSearchQuery(state) {
4
+ var _queryState$current;
5
+
6
+ var queryState = useRef(null);
7
+ /*
8
+ * When state contains a valid url AND is NOT selected from results
9
+ * queryState should be null to clear the items in plugin state
10
+ */
11
+
12
+ if (isSafeUrl(state.url) && state.selectedIndex === -1) {
13
+ queryState.current = null;
14
+ }
15
+ /*
16
+ * When state contains a search term e.g. Not a valid url return a query
17
+ */
18
+
19
+
20
+ if (((_queryState$current = queryState.current) === null || _queryState$current === void 0 ? void 0 : _queryState$current.query) !== state.url && !isSafeUrl(state.url)) {
21
+ queryState.current = {
22
+ query: state.url
23
+ };
24
+ }
25
+ /*
26
+ * When state contains a valid url and is selected from the list
27
+ * return current state to prevent triger a new plugin search
28
+ */
29
+
30
+
31
+ return queryState.current;
32
+ }
@@ -2,7 +2,7 @@
2
2
  import { jsx } from '@emotion/react';
3
3
  import EmptyState from '@atlaskit/empty-state';
4
4
  import { defineMessages, useIntl } from 'react-intl-next';
5
- import GenericErrorSVG from './generic-error-svg';
5
+ import GenericErrorSVG from '../../common/generic-error-svg';
6
6
  import { rootContainerStyles } from '../link-picker/styled';
7
7
  var messages = defineMessages({
8
8
  heading: {
@@ -1,11 +1,10 @@
1
1
  import React, { memo } from 'react';
2
2
  import { AnalyticsErrorBoundary } from '@atlaskit/analytics-next';
3
3
  import { LazySuspense, lazyForPaint } from 'react-loosely-lazy';
4
+ import { COMPONENT_NAME, ANALYTICS_CHANNEL } from '../common/constants';
4
5
  import { name as packageName, version as packageVersion } from '../version.json';
5
6
  import { ErrorBoundaryFallback } from './error-boundary-fallback';
6
7
  import { LoaderFallback } from './loader-fallback';
7
- var COMPONENT_NAME = 'LinkPicker';
8
- var ANALYTICS_CHANNEL = 'media';
9
8
  var PACKAGE_DATA = {
10
9
  packageName: packageName,
11
10
  packageVersion: packageVersion,
@@ -7,25 +7,31 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
7
7
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
8
8
 
9
9
  /** @jsx jsx */
10
- import { useCallback, useLayoutEffect, useReducer } from 'react';
10
+ import { Fragment, useCallback, useLayoutEffect, useReducer } from 'react';
11
11
  import { jsx } from '@emotion/react';
12
12
  import { useIntl, FormattedMessage } from 'react-intl-next';
13
+ import { useAnalyticsEvents } from '@atlaskit/analytics-next';
13
14
  import Button, { ButtonGroup } from '@atlaskit/button';
14
15
  import { FormFooter } from '@atlaskit/form';
15
16
  import EditorSearchIcon from '@atlaskit/icon/glyph/editor/search';
16
17
  import Tabs, { Tab, TabList } from '@atlaskit/tabs';
17
18
  import VisuallyHidden from '@atlaskit/visually-hidden';
19
+ import createEventPayload from '../../analytics.codegen';
20
+ import { ANALYTICS_CHANNEL } from '../../common/constants';
21
+ import { usePlugins } from '../../services/use-plugins';
22
+ import { useSearchQuery } from '../../services/use-search-query';
18
23
  import { messages } from './messages';
19
24
  import PanelTextInput from './text-input';
20
25
  import LinkSearchList from './link-search-list';
21
26
  import { normalizeUrl, isSafeUrl } from './url';
22
27
  import { rootContainerStyles, searchIconStyles, listTitleStyles, tabsWrapperStyles } from './styled';
23
28
  import { browser } from './browser';
24
- import { transformTimeStamp } from './transformTimeStamp';
25
29
  import Announcer from './announcer';
26
30
  import LinkSearchNoResults from './link-search-no-results';
27
- import { usePlugins } from '../../services/use-plugins';
31
+ import { transformTimeStamp } from './transformTimeStamp';
32
+ import LinkSearchError from './link-search-error';
28
33
  export var RECENT_SEARCH_LIST_SIZE = 5;
34
+ export var CONTACT_SUPPORT_LINK = 'https://support.atlassian.com/contact/';
29
35
  export var testIds = {
30
36
  linkPicker: 'link-picker',
31
37
  urlInputField: 'link-url',
@@ -41,7 +47,8 @@ export var testIds = {
41
47
  searchResultLoadingIndicator: 'link-picker.results-loading-indicator',
42
48
  urlError: 'link-error',
43
49
  tabList: 'link-picker-tabs',
44
- tabItem: 'link-picker-tab'
50
+ tabItem: 'link-picker-tab',
51
+ searchError: 'link-search-error'
45
52
  };
46
53
  var initState = {
47
54
  url: '',
@@ -64,6 +71,8 @@ function reducer(state, payload) {
64
71
  }
65
72
 
66
73
  function LinkPicker(_ref) {
74
+ var _queryState$query, _items$length, _errorFallback;
75
+
67
76
  var onSubmit = _ref.onSubmit,
68
77
  onCancel = _ref.onCancel,
69
78
  onContentResize = _ref.onContentResize,
@@ -71,6 +80,9 @@ function LinkPicker(_ref) {
71
80
  initUrl = _ref.url,
72
81
  initDisplayText = _ref.displayText;
73
82
 
83
+ var _useAnalyticsEvents = useAnalyticsEvents(),
84
+ createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
85
+
74
86
  var _useReducer = useReducer(reducer, _objectSpread(_objectSpread({}, initState), {}, {
75
87
  url: normalizeUrl(initUrl) || '',
76
88
  displayText: initDisplayText || ''
@@ -83,17 +95,23 @@ function LinkPicker(_ref) {
83
95
  selectedIndex = state.selectedIndex,
84
96
  url = state.url,
85
97
  displayText = state.displayText,
86
- invalidUrl = state.invalidUrl;
87
- var isEditing = !!initUrl;
98
+ invalidUrl = state.invalidUrl,
99
+ activeTab = state.activeTab;
88
100
  var intl = useIntl();
101
+ var queryState = useSearchQuery(state);
89
102
 
90
- var _usePlugins = usePlugins(state, plugins),
103
+ var _usePlugins = usePlugins(queryState, activeTab, plugins),
91
104
  items = _usePlugins.items,
92
105
  isLoading = _usePlugins.isLoading,
93
- isSelectedItem = _usePlugins.isSelectedItem,
94
106
  isActivePlugin = _usePlugins.isActivePlugin,
95
- tabs = _usePlugins.tabs;
107
+ tabs = _usePlugins.tabs,
108
+ error = _usePlugins.error,
109
+ retry = _usePlugins.retry,
110
+ errorFallback = _usePlugins.errorFallback;
96
111
 
112
+ var isEditing = !!initUrl;
113
+ var selectedItem = items === null || items === void 0 ? void 0 : items[selectedIndex];
114
+ var isSelectedItem = (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.url) === url;
97
115
  useLayoutEffect(function () {
98
116
  if (onContentResize) {
99
117
  onContentResize();
@@ -147,15 +165,15 @@ function LinkPicker(_ref) {
147
165
  dispatch(_defineProperty({}, field, ''));
148
166
  }, []);
149
167
  var handleMouseEnterResultItem = useCallback(function (objectId) {
150
- var index = findIndex(items, function (item) {
168
+ var index = items === null || items === void 0 ? void 0 : items.findIndex(function (item) {
151
169
  return item.objectId === objectId;
152
170
  });
153
171
  dispatch({
154
- activeIndex: index
172
+ activeIndex: index !== null && index !== void 0 ? index : -1
155
173
  });
156
174
  }, [items]);
157
175
  var handleMouseLeaveResultItem = useCallback(function (objectId) {
158
- var index = findIndex(items, function (item) {
176
+ var index = items === null || items === void 0 ? void 0 : items.findIndex(function (item) {
159
177
  return item.objectId === objectId;
160
178
  }); // This is to avoid updating index that was set by other mouseenter event
161
179
 
@@ -166,6 +184,8 @@ function LinkPicker(_ref) {
166
184
  }
167
185
  }, [activeIndex, items]);
168
186
  var handleInsert = useCallback(function (url, title, inputType) {
187
+ var event = createAnalyticsEvent(createEventPayload('form.submitted.linkPicker', {}));
188
+ event.fire(ANALYTICS_CHANNEL);
169
189
  onSubmit(_objectSpread({
170
190
  url: url,
171
191
  displayText: displayText || null,
@@ -176,22 +196,23 @@ function LinkPicker(_ref) {
176
196
  }, inputType === 'manual' ? {
177
197
  rawUrl: state.url
178
198
  } : {}));
179
- }, [displayText, onSubmit, state.url]);
199
+ }, [displayText, onSubmit, state.url, createAnalyticsEvent]);
180
200
  var handleSelected = useCallback(function (objectId) {
181
- var selectedItem = items.find(function (item) {
201
+ var selectedItem = items === null || items === void 0 ? void 0 : items.find(function (item) {
182
202
  return item.objectId === objectId;
183
203
  });
184
204
 
185
205
  if (selectedItem) {
186
206
  var _url = selectedItem.url,
187
207
  name = selectedItem.name;
208
+ dispatchEvent(new Event('submit'));
188
209
  handleInsert(_url, name, 'typeAhead');
189
210
  }
190
211
  }, [handleInsert, items]);
191
- var handleSubmit = useCallback(function () {
192
- var selectedItem = items[selectedIndex];
212
+ var handleSubmit = useCallback(function (event) {
213
+ event.preventDefault();
193
214
 
194
- if (isSelectedItem) {
215
+ if (isSelectedItem && selectedItem) {
195
216
  return handleInsert(selectedItem.url, selectedItem.name, 'typeAhead');
196
217
  }
197
218
 
@@ -204,18 +225,18 @@ function LinkPicker(_ref) {
204
225
  return dispatch({
205
226
  invalidUrl: true
206
227
  });
207
- }, [handleInsert, isSelectedItem, items, selectedIndex, url]);
228
+ }, [handleInsert, isSelectedItem, selectedItem, url]);
208
229
  var linkPlaceHolder = isActivePlugin ? messages.placeholder : messages.linkPlaceholder;
209
- var linkListTitle = url ? messages.titleResults : messages.titleRecentlyViewed;
230
+ var linkListTitle = (queryState === null || queryState === void 0 ? void 0 : (_queryState$query = queryState.query) === null || _queryState$query === void 0 ? void 0 : _queryState$query.length) !== 0 ? messages.titleResults : messages.titleRecentlyViewed;
210
231
  var insertButtonMsg = isEditing ? messages.saveButton : messages.insertButton;
211
- var noResults = isActivePlugin && !isLoading && !items.length && !isSafeUrl(url);
232
+ var noResults = (items === null || items === void 0 ? void 0 : items.length) === 0 && !error;
212
233
  var screenReaderDescriptionId = 'search-recent-links-field-description';
213
234
  var linkSearchListId = 'link-picker-search-list';
214
235
  var ariaActiveDescendant = selectedIndex > -1 ? "link-search-list-item-".concat(selectedIndex) : ''; // Added workaround with a screen reader Announcer specifically for VoiceOver + Safari
215
236
  // as the Aria design pattern for combobox does not work in this case
216
237
  // for details: https://a11y-internal.atlassian.net/browse/AK-740
217
238
 
218
- var screenReaderText = browser.safari && getScreenReaderText(items, selectedIndex, intl);
239
+ var screenReaderText = browser.safari && getScreenReaderText(items !== null && items !== void 0 ? items : [], selectedIndex, intl);
219
240
  var searchIcon = isActivePlugin && jsx("span", {
220
241
  css: searchIconStyles,
221
242
  "data-testid": testIds.searchIcon
@@ -223,9 +244,10 @@ function LinkPicker(_ref) {
223
244
  size: "medium",
224
245
  label: ''
225
246
  }));
226
- return jsx("div", {
247
+ return jsx("form", {
227
248
  "data-testid": testIds.linkPicker,
228
- css: rootContainerStyles
249
+ css: rootContainerStyles,
250
+ onSubmit: handleSubmit
229
251
  }, screenReaderText && jsx(Announcer, {
230
252
  ariaLive: "assertive",
231
253
  text: screenReaderText,
@@ -251,7 +273,6 @@ function LinkPicker(_ref) {
251
273
  "aria-describedby": screenReaderDescriptionId,
252
274
  error: invalidUrl ? intl.formatMessage(messages.linkInvalid) : null,
253
275
  onClear: handleClear,
254
- onSubmit: handleSubmit,
255
276
  onKeyDown: handleKeyDown,
256
277
  onChange: handleChangeUrl
257
278
  }), jsx(PanelTextInput, {
@@ -264,10 +285,9 @@ function LinkPicker(_ref) {
264
285
  clearLabel: intl.formatMessage(messages.clearText),
265
286
  "aria-label": intl.formatMessage(messages.linkAriaLabel),
266
287
  onClear: handleClear,
267
- onSubmit: handleSubmit,
268
288
  onKeyDown: handleKeyDown,
269
289
  onChange: handleChangeText
270
- }), tabs.length > 0 && jsx("div", {
290
+ }), queryState && jsx(Fragment, null, tabs.length > 0 && jsx("div", {
271
291
  css: tabsWrapperStyles
272
292
  }, jsx(Tabs, {
273
293
  id: testIds.tabList,
@@ -285,7 +305,7 @@ function LinkPicker(_ref) {
285
305
  key: tab.tabTitle,
286
306
  testId: testIds.tabItem
287
307
  }, tab.tabTitle);
288
- })))), !!items.length && jsx("div", {
308
+ })))), !!(items !== null && items !== void 0 && items.length) && jsx("div", {
289
309
  css: listTitleStyles,
290
310
  id: testIds.resultListTitle,
291
311
  "data-testid": testIds.resultListTitle
@@ -293,7 +313,7 @@ function LinkPicker(_ref) {
293
313
  id: "fabric.smartcard.linkpicker.suggested.results"
294
314
  }, url && jsx(FormattedMessage, _extends({}, messages.searchLinkResults, {
295
315
  values: {
296
- count: items.length
316
+ count: (_items$length = items === null || items === void 0 ? void 0 : items.length) !== null && _items$length !== void 0 ? _items$length : 0
297
317
  },
298
318
  "aria-live": "polite",
299
319
  "aria-atomic": "true"
@@ -302,18 +322,34 @@ function LinkPicker(_ref) {
302
322
  ariaControls: "fabric.smartcard.linkpicker.suggested.results",
303
323
  id: linkSearchListId,
304
324
  role: "listbox",
305
- items: items,
325
+ items: items !== null && items !== void 0 ? items : [],
306
326
  isLoading: isLoading,
307
327
  selectedIndex: selectedIndex,
308
328
  activeIndex: activeIndex,
309
329
  onSelect: handleSelected,
310
330
  onMouseEnter: handleMouseEnterResultItem,
311
331
  onMouseLeave: handleMouseLeaveResultItem
312
- }), noResults && jsx(LinkSearchNoResults, {
332
+ }), error && ((_errorFallback = errorFallback === null || errorFallback === void 0 ? void 0 : errorFallback(error, retry)) !== null && _errorFallback !== void 0 ? _errorFallback : jsx(LinkSearchError, {
333
+ testId: testIds.searchError,
334
+ header: intl.formatMessage(messages.searchErrorHeader),
335
+ description: jsx(FormattedMessage, _extends({}, messages.searchErrorDescription, {
336
+ values: {
337
+ a: function a(label) {
338
+ return jsx(Button, {
339
+ appearance: "link",
340
+ spacing: "none",
341
+ href: CONTACT_SUPPORT_LINK,
342
+ target: "_blank",
343
+ rel: "noopener noreferrer"
344
+ }, label);
345
+ }
346
+ }
347
+ }))
348
+ })), noResults && jsx(LinkSearchNoResults, {
313
349
  testId: testIds.emptyResultPage,
314
350
  header: intl.formatMessage(messages.noResults),
315
351
  description: intl.formatMessage(messages.noResultsDescription)
316
- }), jsx(FormFooter, {
352
+ })), jsx(FormFooter, {
317
353
  align: "end"
318
354
  }, jsx(ButtonGroup, null, jsx(Button, {
319
355
  appearance: "default",
@@ -322,7 +358,6 @@ function LinkPicker(_ref) {
322
358
  }, intl.formatMessage(messages.cancelButton)), jsx(Button, {
323
359
  type: "submit",
324
360
  appearance: "primary",
325
- onClick: handleSubmit,
326
361
  testId: testIds.insertButton
327
362
  }, intl.formatMessage(insertButtonMsg)))));
328
363
  }
@@ -340,17 +375,4 @@ function getScreenReaderText(items, selectedIndex, intl) {
340
375
  var formattedDate = [date === null || date === void 0 ? void 0 : date.pageAction, date === null || date === void 0 ? void 0 : date.dateString, date === null || date === void 0 ? void 0 : date.timeSince].filter(Boolean).join(' ');
341
376
  return [name, container, formattedDate].filter(Boolean).join(', ');
342
377
  }
343
- }
344
-
345
- function findIndex(array, predicate) {
346
- var index = -1;
347
- array.some(function (item, i) {
348
- if (predicate(item)) {
349
- index = i;
350
- return true;
351
- }
352
-
353
- return false;
354
- });
355
- return index;
356
378
  }
@@ -0,0 +1,14 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import React from 'react';
3
+ import EmptyState from '@atlaskit/empty-state';
4
+ import GenericErrorSVG from '../../../common/generic-error-svg';
5
+
6
+ var LinkSearchError = function LinkSearchError(props) {
7
+ return /*#__PURE__*/React.createElement(EmptyState, _extends({}, props, {
8
+ renderImage: function renderImage() {
9
+ return /*#__PURE__*/React.createElement(GenericErrorSVG, null);
10
+ }
11
+ }));
12
+ };
13
+
14
+ export default LinkSearchError;
@@ -104,5 +104,15 @@ export var messages = defineMessages({
104
104
  id: 'fabric.linkPicker.button.save',
105
105
  defaultMessage: 'Save',
106
106
  description: 'Button to save edited link'
107
+ },
108
+ searchErrorHeader: {
109
+ id: 'fabric.linkPicker.search.error.heading',
110
+ defaultMessage: 'We’re having trouble loading data.',
111
+ description: 'Heading message shown when a search throws an error'
112
+ },
113
+ searchErrorDescription: {
114
+ id: 'fabric.linkPicker.search.error.description',
115
+ defaultMessage: 'Refresh the page, or contact <a>Atlassian Support</a> if this keeps happening.',
116
+ description: 'Describes possible actions when search throws an error'
107
117
  }
108
118
  });
@@ -1,6 +1,6 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
2
  import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
3
- var _excluded = ["name", "label", "autoFocus", "onSubmit", "onRedo", "onUndo", "onChange", "onKeyDown", "onClear", "clearLabel", "error"];
3
+ var _excluded = ["name", "label", "autoFocus", "onRedo", "onUndo", "onChange", "onKeyDown", "onClear", "clearLabel", "error"];
4
4
 
5
5
  /** @jsx jsx */
6
6
  import { useCallback, useRef, Fragment } from 'react';
@@ -17,7 +17,6 @@ var PanelTextInput = function PanelTextInput(props) {
17
17
  var name = props.name,
18
18
  label = props.label,
19
19
  autoFocus = props.autoFocus,
20
- onSubmit = props.onSubmit,
21
20
  onRedo = props.onRedo,
22
21
  onUndo = props.onUndo,
23
22
  onChange = props.onChange,
@@ -44,13 +43,7 @@ var PanelTextInput = function PanelTextInput(props) {
44
43
  onChange(e.currentTarget.value);
45
44
  }, [onChange]);
46
45
  var handleKeydown = useCallback(function (e) {
47
- var value = e.currentTarget.value;
48
-
49
- if (e.keyCode === 13 && onSubmit) {
50
- e.preventDefault(); // Prevent from submitting if an editor is inside a form.
51
-
52
- onSubmit(value);
53
- } else if (typeof onUndo === 'function' && isUndoEvent(e)) {
46
+ if (typeof onUndo === 'function' && isUndoEvent(e)) {
54
47
  e.preventDefault();
55
48
  onUndo();
56
49
  } else if (typeof onRedo === 'function' && isRedoEvent(e)) {
@@ -61,7 +54,7 @@ var PanelTextInput = function PanelTextInput(props) {
61
54
  if (onKeyDown) {
62
55
  onKeyDown(e);
63
56
  }
64
- }, [onSubmit, onUndo, onRedo, onKeyDown]);
57
+ }, [onUndo, onRedo, onKeyDown]);
65
58
  var handleClear = useCallback(function () {
66
59
  if (inputRef.current && onClear) {
67
60
  inputRef.current.focus();
@@ -71,6 +64,7 @@ var PanelTextInput = function PanelTextInput(props) {
71
64
  var clearText = restProps.value !== '' && jsx(Tooltip, {
72
65
  content: clearLabel
73
66
  }, jsx("button", {
67
+ type: "button",
74
68
  css: clearTextButtonStyles,
75
69
  onClick: handleClear,
76
70
  "data-testid": testIds.clearUrlButton
@@ -64,11 +64,6 @@ var linkifyMatch = function linkifyMatch(text) {
64
64
 
65
65
  export var isSafeUrl = function isSafeUrl(url) {
66
66
  var urlTrimmed = url.trim();
67
-
68
- if (urlTrimmed.length === 0) {
69
- return true;
70
- }
71
-
72
67
  return whitelistedURLPatterns.some(function (p) {
73
68
  return p.test(urlTrimmed);
74
69
  });
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/link-picker",
3
- "version": "1.6.1",
3
+ "version": "1.8.0",
4
4
  "sideEffects": false
5
5
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
+ *
4
+ * Generates analytics utilities from the package analytics spec yaml
5
+ *
6
+ * @codegen <<SignedSource::0cc1b4d9c22b31491ded3e581114ab2c>>
7
+ * @codegenCommand yarn workspace @atlaskit/link-picker run codegen-analytics
8
+ */
9
+ declare type FormSubmittedLinkPickerAttributesType = {};
10
+ declare type AnalyticsEventAttributes = {
11
+ 'form.submitted.linkPicker': FormSubmittedLinkPickerAttributesType;
12
+ };
13
+ declare function createEventPayload<K extends keyof AnalyticsEventAttributes>(eventKey: K, attributes: AnalyticsEventAttributes[K]): {
14
+ eventType: string;
15
+ actionSubject: string;
16
+ action: string;
17
+ actionSubjectId: string;
18
+ attributes: AnalyticsEventAttributes[K];
19
+ };
20
+ export default createEventPayload;
@@ -0,0 +1,2 @@
1
+ export declare const ANALYTICS_CHANNEL = "media";
2
+ export declare const COMPONENT_NAME = "LinkPicker";
@@ -1,3 +1,3 @@
1
1
  export { ComposedLinkPicker as LinkPicker } from './ui';
2
2
  export type { LinkPickerProps } from './ui/link-picker';
3
- export type { LinkInputType, LinkPickerPlugin, LinkPickerState, LinkSearchListItemData, } from './ui/types';
3
+ export type { LinkInputType, LinkPickerPlugin, LinkPickerState, LinkSearchListItemData, LinkPickerPluginErrorFallback, } from './ui/types';
@@ -1,12 +1,13 @@
1
- import { PickerState } from '../../ui/link-picker';
2
- import { LinkPickerPlugin, LinkSearchListItemData } from '../../ui/types';
1
+ import { LinkPickerPlugin, LinkPickerPluginErrorFallback, LinkPickerState, LinkSearchListItemData } from '../../ui/types';
3
2
  export interface LinkPickerPluginsService {
4
- items: LinkSearchListItemData[];
3
+ items: LinkSearchListItemData[] | null;
5
4
  isLoading: boolean;
6
- isSelectedItem: boolean;
7
5
  isActivePlugin: boolean;
8
6
  tabs: {
9
7
  tabTitle: string;
10
8
  }[];
9
+ error: unknown | null;
10
+ retry: () => void;
11
+ errorFallback?: LinkPickerPluginErrorFallback;
11
12
  }
12
- export declare function usePlugins(state: PickerState, plugins?: LinkPickerPlugin[]): LinkPickerPluginsService;
13
+ export declare function usePlugins(state: LinkPickerState | null, activeTab: number, plugins?: LinkPickerPlugin[]): LinkPickerPluginsService;
@@ -0,0 +1,3 @@
1
+ import { LinkPickerState } from '../../ui/types';
2
+ import { PickerState } from '../../ui/link-picker';
3
+ export declare function useSearchQuery(state: PickerState): LinkPickerState | null;
@@ -1,6 +1,7 @@
1
1
  import { jsx } from '@emotion/react';
2
2
  import { LinkInputType, LinkPickerPlugin } from '../types';
3
3
  export declare const RECENT_SEARCH_LIST_SIZE = 5;
4
+ export declare const CONTACT_SUPPORT_LINK = "https://support.atlassian.com/contact/";
4
5
  export declare const testIds: {
5
6
  linkPicker: string;
6
7
  urlInputField: string;
@@ -17,6 +18,7 @@ export declare const testIds: {
17
18
  urlError: string;
18
19
  tabList: string;
19
20
  tabItem: string;
21
+ searchError: string;
20
22
  };
21
23
  interface Meta {
22
24
  /** Indicates how the link was picked. */
@@ -0,0 +1,4 @@
1
+ /// <reference types="react" />
2
+ import { EmptyStateProps } from '@atlaskit/empty-state';
3
+ declare const LinkSearchError: (props: EmptyStateProps) => JSX.Element;
4
+ export default LinkSearchError;
@@ -104,4 +104,14 @@ export declare const messages: {
104
104
  defaultMessage: string;
105
105
  description: string;
106
106
  };
107
+ searchErrorHeader: {
108
+ id: string;
109
+ defaultMessage: string;
110
+ description: string;
111
+ };
112
+ searchErrorDescription: {
113
+ id: string;
114
+ defaultMessage: string;
115
+ description: string;
116
+ };
107
117
  };