@atlaskit/link-picker 1.6.0 → 1.7.1

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 (53) 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 +68 -41
  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/url.js +0 -5
  14. package/dist/cjs/version.json +1 -1
  15. package/dist/es2019/analytics.codegen.js +20 -0
  16. package/dist/es2019/common/constants.js +2 -0
  17. package/dist/es2019/{ui/error-boundary-fallback → common}/generic-error-svg/index.js +0 -0
  18. package/dist/es2019/{ui/error-boundary-fallback → common}/generic-error-svg/styled.js +0 -0
  19. package/dist/es2019/services/use-plugins/index.js +28 -38
  20. package/dist/es2019/services/use-search-query/index.js +32 -0
  21. package/dist/es2019/ui/error-boundary-fallback/index.js +1 -1
  22. package/dist/es2019/ui/index.js +1 -2
  23. package/dist/es2019/ui/link-picker/index.js +59 -40
  24. package/dist/es2019/ui/link-picker/link-search-error/index.js +10 -0
  25. package/dist/es2019/ui/link-picker/messages.js +10 -0
  26. package/dist/es2019/ui/link-picker/url.js +0 -5
  27. package/dist/es2019/version.json +1 -1
  28. package/dist/esm/analytics.codegen.js +20 -0
  29. package/dist/esm/common/constants.js +2 -0
  30. package/dist/esm/{ui/error-boundary-fallback → common}/generic-error-svg/index.js +0 -0
  31. package/dist/esm/{ui/error-boundary-fallback → common}/generic-error-svg/styled.js +0 -0
  32. package/dist/esm/services/use-plugins/index.js +33 -35
  33. package/dist/esm/services/use-search-query/index.js +32 -0
  34. package/dist/esm/ui/error-boundary-fallback/index.js +1 -1
  35. package/dist/esm/ui/index.js +1 -2
  36. package/dist/esm/ui/link-picker/index.js +61 -40
  37. package/dist/esm/ui/link-picker/link-search-error/index.js +14 -0
  38. package/dist/esm/ui/link-picker/messages.js +10 -0
  39. package/dist/esm/ui/link-picker/url.js +0 -5
  40. package/dist/esm/version.json +1 -1
  41. package/dist/types/analytics.codegen.d.ts +20 -0
  42. package/dist/types/common/constants.d.ts +2 -0
  43. package/dist/types/{ui/error-boundary-fallback → common}/generic-error-svg/index.d.ts +0 -0
  44. package/dist/types/{ui/error-boundary-fallback → common}/generic-error-svg/styled.d.ts +0 -0
  45. package/dist/types/index.d.ts +1 -1
  46. package/dist/types/services/use-plugins/index.d.ts +6 -5
  47. package/dist/types/services/use-search-query/index.d.ts +3 -0
  48. package/dist/types/ui/link-picker/index.d.ts +2 -0
  49. package/dist/types/ui/link-picker/link-search-error/index.d.ts +4 -0
  50. package/dist/types/ui/link-picker/messages.d.ts +10 -0
  51. package/dist/types/ui/types.d.ts +5 -2
  52. package/package.json +12 -6
  53. package/report.api.md +21 -13
@@ -1,6 +1,5 @@
1
- import { useEffect, useRef, useReducer, useMemo } from 'react';
1
+ import { useEffect, useReducer, useMemo, useRef, useState, useCallback } from 'react';
2
2
  import { RECENT_SEARCH_LIST_SIZE } from '../../ui/link-picker';
3
- import { isSafeUrl } from '../../ui/link-picker/url';
4
3
 
5
4
  function reducer(state, payload) {
6
5
  return { ...state,
@@ -8,56 +7,40 @@ function reducer(state, payload) {
8
7
  };
9
8
  }
10
9
 
11
- export function usePlugins(state, plugins) {
10
+ export function usePlugins(state, activeTab, plugins) {
11
+ const [retries, setRetries] = useState(0);
12
12
  const [pluginState, dispatch] = useReducer(reducer, {
13
- items: [],
13
+ items: null,
14
+ error: null,
14
15
  isLoading: false
15
16
  });
16
- const {
17
- url,
18
- selectedIndex
19
- } = state;
20
17
  const {
21
18
  items,
22
- isLoading
19
+ isLoading,
20
+ error
23
21
  } = pluginState;
24
- const activePlugin = !plugins || plugins.length === 0 ? null : plugins[state.activeTab];
25
22
  const queryVersion = useRef(0);
26
- const pluginRef = useRef(activePlugin);
27
- const selectedItem = items[selectedIndex];
28
- const isSelectedItem = (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.url) === url;
23
+ const activePlugin = plugins === null || plugins === void 0 ? void 0 : plugins[activeTab];
29
24
  useEffect(() => {
30
25
  if (!activePlugin) {
31
26
  return;
32
27
  }
33
28
 
34
- if (isSelectedItem) {
35
- return;
36
- }
37
-
38
- if (isSafeUrl(url) && url.length) {
29
+ if (!state) {
39
30
  dispatch({
40
- items: [],
41
- isLoading: false
31
+ items: null,
32
+ isLoading: false,
33
+ error: null
42
34
  });
43
35
  return;
44
36
  }
45
37
 
46
- const updates = activePlugin.resolve({
47
- query: url
38
+ const updates = activePlugin.resolve(state);
39
+ dispatch({
40
+ items: null,
41
+ isLoading: true,
42
+ error: null
48
43
  });
49
-
50
- if (pluginRef.current !== activePlugin) {
51
- dispatch({
52
- items: [],
53
- isLoading: true
54
- });
55
- } else {
56
- dispatch({
57
- isLoading: true
58
- });
59
- }
60
-
61
44
  const newQuery = ++queryVersion.current;
62
45
 
63
46
  const updateResults = async () => {
@@ -90,15 +73,17 @@ export function usePlugins(state, plugins) {
90
73
  return;
91
74
  }
92
75
  }
93
- } catch {
76
+ } catch (error) {
94
77
  dispatch({
78
+ error,
79
+ items: null,
95
80
  isLoading: false
96
81
  });
97
82
  }
98
83
  };
99
84
 
100
85
  updateResults();
101
- }, [activePlugin, queryVersion, url, isSelectedItem]);
86
+ }, [activePlugin, queryVersion, state, retries]);
102
87
  const tabs = useMemo(() => {
103
88
  if (!plugins || plugins.length <= 1) {
104
89
  return [];
@@ -108,12 +93,17 @@ export function usePlugins(state, plugins) {
108
93
  tabTitle: plugin.tabTitle
109
94
  }));
110
95
  }, [plugins]);
96
+ const handleRetry = useCallback(() => {
97
+ setRetries(prev => ++prev);
98
+ }, []);
111
99
  return {
112
100
  tabs,
113
101
  items,
114
102
  isLoading,
115
- isSelectedItem,
116
- isActivePlugin: !!activePlugin
103
+ isActivePlugin: !!activePlugin,
104
+ error,
105
+ retry: handleRetry,
106
+ errorFallback: activePlugin === null || activePlugin === void 0 ? void 0 : activePlugin.errorFallback
117
107
  };
118
108
  }
119
109
 
@@ -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
+ const 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
  const 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
- const COMPONENT_NAME = 'LinkPicker';
8
- const ANALYTICS_CHANNEL = 'media';
9
8
  const PACKAGE_DATA = {
10
9
  packageName,
11
10
  packageVersion,
@@ -1,25 +1,31 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
2
 
3
3
  /** @jsx jsx */
4
- import { useCallback, useLayoutEffect, useReducer } from 'react';
4
+ import { Fragment, useCallback, useLayoutEffect, useReducer } from 'react';
5
5
  import { jsx } from '@emotion/react';
6
6
  import { useIntl, FormattedMessage } from 'react-intl-next';
7
+ import { useAnalyticsEvents } from '@atlaskit/analytics-next';
7
8
  import Button, { ButtonGroup } from '@atlaskit/button';
8
9
  import { FormFooter } from '@atlaskit/form';
9
10
  import EditorSearchIcon from '@atlaskit/icon/glyph/editor/search';
10
11
  import Tabs, { Tab, TabList } from '@atlaskit/tabs';
11
12
  import VisuallyHidden from '@atlaskit/visually-hidden';
13
+ import createEventPayload from '../../analytics.codegen';
14
+ import { ANALYTICS_CHANNEL } from '../../common/constants';
15
+ import { usePlugins } from '../../services/use-plugins';
16
+ import { useSearchQuery } from '../../services/use-search-query';
12
17
  import { messages } from './messages';
13
18
  import PanelTextInput from './text-input';
14
19
  import LinkSearchList from './link-search-list';
15
20
  import { normalizeUrl, isSafeUrl } from './url';
16
21
  import { rootContainerStyles, searchIconStyles, listTitleStyles, tabsWrapperStyles } from './styled';
17
22
  import { browser } from './browser';
18
- import { transformTimeStamp } from './transformTimeStamp';
19
23
  import Announcer from './announcer';
20
24
  import LinkSearchNoResults from './link-search-no-results';
21
- import { usePlugins } from '../../services/use-plugins';
25
+ import { transformTimeStamp } from './transformTimeStamp';
26
+ import LinkSearchError from './link-search-error';
22
27
  export const RECENT_SEARCH_LIST_SIZE = 5;
28
+ export const CONTACT_SUPPORT_LINK = 'https://support.atlassian.com/contact/';
23
29
  export const testIds = {
24
30
  linkPicker: 'link-picker',
25
31
  urlInputField: 'link-url',
@@ -35,7 +41,8 @@ export const testIds = {
35
41
  searchResultLoadingIndicator: 'link-picker.results-loading-indicator',
36
42
  urlError: 'link-error',
37
43
  tabList: 'link-picker-tabs',
38
- tabItem: 'link-picker-tab'
44
+ tabItem: 'link-picker-tab',
45
+ searchError: 'link-search-error'
39
46
  };
40
47
  const initState = {
41
48
  url: '',
@@ -68,6 +75,11 @@ function LinkPicker({
68
75
  url: initUrl,
69
76
  displayText: initDisplayText
70
77
  }) {
78
+ var _queryState$query, _items$length, _errorFallback;
79
+
80
+ const {
81
+ createAnalyticsEvent
82
+ } = useAnalyticsEvents();
71
83
  const [state, dispatch] = useReducer(reducer, { ...initState,
72
84
  url: normalizeUrl(initUrl) || '',
73
85
  displayText: initDisplayText || ''
@@ -77,17 +89,23 @@ function LinkPicker({
77
89
  selectedIndex,
78
90
  url,
79
91
  displayText,
80
- invalidUrl
92
+ invalidUrl,
93
+ activeTab
81
94
  } = state;
82
- const isEditing = !!initUrl;
83
95
  const intl = useIntl();
96
+ const queryState = useSearchQuery(state);
84
97
  const {
85
98
  items,
86
99
  isLoading,
87
- isSelectedItem,
88
100
  isActivePlugin,
89
- tabs
90
- } = usePlugins(state, plugins);
101
+ tabs,
102
+ error,
103
+ retry,
104
+ errorFallback
105
+ } = usePlugins(queryState, activeTab, plugins);
106
+ const isEditing = !!initUrl;
107
+ const selectedItem = items === null || items === void 0 ? void 0 : items[selectedIndex];
108
+ const isSelectedItem = (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.url) === url;
91
109
  useLayoutEffect(() => {
92
110
  if (onContentResize) {
93
111
  onContentResize();
@@ -145,13 +163,13 @@ function LinkPicker({
145
163
  });
146
164
  }, []);
147
165
  const handleMouseEnterResultItem = useCallback(objectId => {
148
- const index = findIndex(items, item => item.objectId === objectId);
166
+ const index = items === null || items === void 0 ? void 0 : items.findIndex(item => item.objectId === objectId);
149
167
  dispatch({
150
- activeIndex: index
168
+ activeIndex: index !== null && index !== void 0 ? index : -1
151
169
  });
152
170
  }, [items]);
153
171
  const handleMouseLeaveResultItem = useCallback(objectId => {
154
- const index = findIndex(items, item => item.objectId === objectId); // This is to avoid updating index that was set by other mouseenter event
172
+ const index = items === null || items === void 0 ? void 0 : items.findIndex(item => item.objectId === objectId); // This is to avoid updating index that was set by other mouseenter event
155
173
 
156
174
  if (activeIndex === index) {
157
175
  dispatch({
@@ -160,6 +178,8 @@ function LinkPicker({
160
178
  }
161
179
  }, [activeIndex, items]);
162
180
  const handleInsert = useCallback((url, title, inputType) => {
181
+ const event = createAnalyticsEvent(createEventPayload('form.submitted.linkPicker', {}));
182
+ event.fire(ANALYTICS_CHANNEL);
163
183
  onSubmit({
164
184
  url,
165
185
  displayText: displayText || null,
@@ -171,9 +191,9 @@ function LinkPicker({
171
191
  rawUrl: state.url
172
192
  } : {})
173
193
  });
174
- }, [displayText, onSubmit, state.url]);
194
+ }, [displayText, onSubmit, state.url, createAnalyticsEvent]);
175
195
  const handleSelected = useCallback(objectId => {
176
- const selectedItem = items.find(item => item.objectId === objectId);
196
+ const selectedItem = items === null || items === void 0 ? void 0 : items.find(item => item.objectId === objectId);
177
197
 
178
198
  if (selectedItem) {
179
199
  const {
@@ -184,9 +204,7 @@ function LinkPicker({
184
204
  }
185
205
  }, [handleInsert, items]);
186
206
  const handleSubmit = useCallback(() => {
187
- const selectedItem = items[selectedIndex];
188
-
189
- if (isSelectedItem) {
207
+ if (isSelectedItem && selectedItem) {
190
208
  return handleInsert(selectedItem.url, selectedItem.name, 'typeAhead');
191
209
  }
192
210
 
@@ -199,18 +217,18 @@ function LinkPicker({
199
217
  return dispatch({
200
218
  invalidUrl: true
201
219
  });
202
- }, [handleInsert, isSelectedItem, items, selectedIndex, url]);
220
+ }, [handleInsert, isSelectedItem, selectedItem, url]);
203
221
  const linkPlaceHolder = isActivePlugin ? messages.placeholder : messages.linkPlaceholder;
204
- const linkListTitle = url ? messages.titleResults : messages.titleRecentlyViewed;
222
+ const 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;
205
223
  const insertButtonMsg = isEditing ? messages.saveButton : messages.insertButton;
206
- const noResults = isActivePlugin && !isLoading && !items.length && !isSafeUrl(url);
224
+ const noResults = (items === null || items === void 0 ? void 0 : items.length) === 0 && !error;
207
225
  const screenReaderDescriptionId = 'search-recent-links-field-description';
208
226
  const linkSearchListId = 'link-picker-search-list';
209
227
  const ariaActiveDescendant = selectedIndex > -1 ? `link-search-list-item-${selectedIndex}` : ''; // Added workaround with a screen reader Announcer specifically for VoiceOver + Safari
210
228
  // as the Aria design pattern for combobox does not work in this case
211
229
  // for details: https://a11y-internal.atlassian.net/browse/AK-740
212
230
 
213
- const screenReaderText = browser.safari && getScreenReaderText(items, selectedIndex, intl);
231
+ const screenReaderText = browser.safari && getScreenReaderText(items !== null && items !== void 0 ? items : [], selectedIndex, intl);
214
232
  const searchIcon = isActivePlugin && jsx("span", {
215
233
  css: searchIconStyles,
216
234
  "data-testid": testIds.searchIcon
@@ -262,7 +280,7 @@ function LinkPicker({
262
280
  onSubmit: handleSubmit,
263
281
  onKeyDown: handleKeyDown,
264
282
  onChange: handleChangeText
265
- }), tabs.length > 0 && jsx("div", {
283
+ }), queryState && jsx(Fragment, null, tabs.length > 0 && jsx("div", {
266
284
  css: tabsWrapperStyles
267
285
  }, jsx(Tabs, {
268
286
  id: testIds.tabList,
@@ -276,7 +294,7 @@ function LinkPicker({
276
294
  }, jsx(TabList, null, tabs.map(tab => jsx(Tab, {
277
295
  key: tab.tabTitle,
278
296
  testId: testIds.tabItem
279
- }, tab.tabTitle))))), !!items.length && jsx("div", {
297
+ }, tab.tabTitle))))), !!(items !== null && items !== void 0 && items.length) && jsx("div", {
280
298
  css: listTitleStyles,
281
299
  id: testIds.resultListTitle,
282
300
  "data-testid": testIds.resultListTitle
@@ -284,7 +302,7 @@ function LinkPicker({
284
302
  id: "fabric.smartcard.linkpicker.suggested.results"
285
303
  }, url && jsx(FormattedMessage, _extends({}, messages.searchLinkResults, {
286
304
  values: {
287
- count: items.length
305
+ count: (_items$length = items === null || items === void 0 ? void 0 : items.length) !== null && _items$length !== void 0 ? _items$length : 0
288
306
  },
289
307
  "aria-live": "polite",
290
308
  "aria-atomic": "true"
@@ -293,18 +311,32 @@ function LinkPicker({
293
311
  ariaControls: "fabric.smartcard.linkpicker.suggested.results",
294
312
  id: linkSearchListId,
295
313
  role: "listbox",
296
- items: items,
314
+ items: items !== null && items !== void 0 ? items : [],
297
315
  isLoading: isLoading,
298
316
  selectedIndex: selectedIndex,
299
317
  activeIndex: activeIndex,
300
318
  onSelect: handleSelected,
301
319
  onMouseEnter: handleMouseEnterResultItem,
302
320
  onMouseLeave: handleMouseLeaveResultItem
303
- }), noResults && jsx(LinkSearchNoResults, {
321
+ }), error && ((_errorFallback = errorFallback === null || errorFallback === void 0 ? void 0 : errorFallback(error, retry)) !== null && _errorFallback !== void 0 ? _errorFallback : jsx(LinkSearchError, {
322
+ testId: testIds.searchError,
323
+ header: intl.formatMessage(messages.searchErrorHeader),
324
+ description: jsx(FormattedMessage, _extends({}, messages.searchErrorDescription, {
325
+ values: {
326
+ a: label => jsx(Button, {
327
+ appearance: "link",
328
+ spacing: "none",
329
+ href: CONTACT_SUPPORT_LINK,
330
+ target: "_blank",
331
+ rel: "noopener noreferrer"
332
+ }, label)
333
+ }
334
+ }))
335
+ })), noResults && jsx(LinkSearchNoResults, {
304
336
  testId: testIds.emptyResultPage,
305
337
  header: intl.formatMessage(messages.noResults),
306
338
  description: intl.formatMessage(messages.noResultsDescription)
307
- }), jsx(FormFooter, {
339
+ })), jsx(FormFooter, {
308
340
  align: "end"
309
341
  }, jsx(ButtonGroup, null, jsx(Button, {
310
342
  appearance: "default",
@@ -332,17 +364,4 @@ function getScreenReaderText(items, selectedIndex, intl) {
332
364
  const 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(' ');
333
365
  return [name, container, formattedDate].filter(Boolean).join(', ');
334
366
  }
335
- }
336
-
337
- function findIndex(array, predicate) {
338
- let index = -1;
339
- array.some((item, i) => {
340
- if (predicate(item)) {
341
- index = i;
342
- return true;
343
- }
344
-
345
- return false;
346
- });
347
- return index;
348
367
  }
@@ -0,0 +1,10 @@
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
+ const LinkSearchError = props => /*#__PURE__*/React.createElement(EmptyState, _extends({}, props, {
7
+ renderImage: () => /*#__PURE__*/React.createElement(GenericErrorSVG, null)
8
+ }));
9
+
10
+ export default LinkSearchError;
@@ -104,5 +104,15 @@ export const 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
  });
@@ -64,11 +64,6 @@ const linkifyMatch = text => {
64
64
 
65
65
  export const isSafeUrl = url => {
66
66
  const urlTrimmed = url.trim();
67
-
68
- if (urlTrimmed.length === 0) {
69
- return true;
70
- }
71
-
72
67
  return whitelistedURLPatterns.some(p => p.test(urlTrimmed));
73
68
  };
74
69
  /**
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/link-picker",
3
- "version": "1.6.0",
3
+ "version": "1.7.1",
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
+ function createEventPayload(eventKey, attributes) {
10
+ var event = eventKey.split('.');
11
+ return {
12
+ eventType: 'ui',
13
+ actionSubject: event[0],
14
+ action: event[1],
15
+ actionSubjectId: event[2],
16
+ attributes: attributes
17
+ };
18
+ }
19
+
20
+ export default createEventPayload;
@@ -0,0 +1,2 @@
1
+ export var ANALYTICS_CHANNEL = 'media';
2
+ export var COMPONENT_NAME = 'LinkPicker';
@@ -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,