@atlaskit/link-picker 1.4.1 → 1.5.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.
@@ -1,10 +1,9 @@
1
- import _defineProperty from "@babel/runtime/helpers/defineProperty";
1
+ import _extends from "@babel/runtime/helpers/extends";
2
2
 
3
3
  /** @jsx jsx */
4
- import { PureComponent } from 'react';
4
+ import { useCallback, useReducer } from 'react';
5
5
  import { jsx } from '@emotion/react';
6
- import { injectIntl } from 'react-intl-next';
7
- import Tabs, { Tab, TabList } from '@atlaskit/tabs';
6
+ import { useIntl, FormattedMessage } from 'react-intl-next';
8
7
  import { messages } from './messages';
9
8
  import PanelTextInput from './text-input';
10
9
  import LinkSearchList from './link-search-list';
@@ -16,489 +15,315 @@ import Announcer from './/announcer';
16
15
  import Button, { ButtonGroup } from '@atlaskit/button';
17
16
  import { FormFooter } from '@atlaskit/form';
18
17
  import EditorSearchIcon from '@atlaskit/icon/glyph/editor/search';
18
+ import Tabs, { Tab, TabList } from '@atlaskit/tabs';
19
19
  import VisuallyHidden from '@atlaskit/visually-hidden';
20
20
  import LinkSearchNoResults from './link-search-no-results';
21
+ import { usePlugins } from '../../services/use-plugins';
21
22
  export const RECENT_SEARCH_LIST_SIZE = 5;
23
+ export const testIds = {
24
+ linkPicker: 'link-picker',
25
+ urlInputField: 'link-url',
26
+ textInputField: 'link-text',
27
+ searchIcon: 'link-picker-search-icon',
28
+ insertButton: 'link-picker-insert-button',
29
+ cancelButton: 'link-picker-cancel-button',
30
+ clearUrlButton: 'clear-text',
31
+ resultListTitle: 'link-picker-list-title',
32
+ emptyResultPage: 'link-search-no-results',
33
+ searchResultList: 'link-search-list',
34
+ searchResultItem: 'link-search-list-item',
35
+ searchResultLoadingIndicator: 'link-picker.results-loading-indicator',
36
+ urlError: 'link-error',
37
+ tabList: 'link-picker-tabs',
38
+ tabItem: 'link-picker-tab'
39
+ };
40
+ const initState = {
41
+ url: '',
42
+ displayText: '',
43
+ activeIndex: -1,
44
+ selectedIndex: -1,
45
+ invalidUrl: false,
46
+ activeTab: 0
47
+ };
48
+
49
+ function reducer(state, payload) {
50
+ if (payload.url && state.url !== payload.url) {
51
+ return { ...state,
52
+ invalidUrl: false,
53
+ selectedIndex: isSafeUrl(payload.url) && payload.url.length ? -1 : state.selectedIndex,
54
+ ...payload
55
+ };
56
+ }
22
57
 
23
- class LinkPicker extends PureComponent {
24
- constructor(props) {
25
- super(props);
26
-
27
- _defineProperty(this, "queryVersion", 0);
28
-
29
- _defineProperty(this, "updateInput", async input => {
30
- // If input is a valid URL, do not search or display link suggestions
31
- if (input.length && isSafeUrl(input)) {
32
- return this.setState({
33
- url: input,
34
- selectedIndex: -1,
35
- items: [],
36
- isLoading: false,
37
- invalidUrl: null
38
- });
39
- }
40
-
41
- return this.setState({
42
- url: input,
43
- // Unset selection on any input change
44
- selectedIndex: -1,
45
- invalidUrl: null
46
- }, () => {
47
- this.updateResults();
48
- });
49
- });
50
-
51
- _defineProperty(this, "createClearHandler", field => {
52
- return () => {
53
- switch (field) {
54
- case 'url':
55
- {
56
- this.updateInput('');
57
- break;
58
- }
59
-
60
- case 'displayText':
61
- {
62
- this.setState({
63
- [field]: ''
64
- });
65
- }
66
- }
67
- };
68
- });
69
-
70
- _defineProperty(this, "getScreenReaderText", () => {
71
- const {
72
- intl
73
- } = this.props;
74
- const {
75
- items,
76
- selectedIndex
77
- } = this.state;
78
-
79
- if (items.length && selectedIndex > -1) {
80
- const {
81
- name,
82
- container,
83
- lastUpdatedDate,
84
- lastViewedDate
85
- } = items[selectedIndex];
86
- const date = transformTimeStamp(intl, lastViewedDate, lastUpdatedDate);
87
- 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(' ');
88
- return [name, container, formattedDate].filter(Boolean).join(', ');
89
- }
90
- });
91
-
92
- _defineProperty(this, "getTabs", () => {
93
- if (this.props.plugins && this.props.plugins.length > 1) {
94
- return this.props.plugins.filter(plugin => !!plugin.tabTitle).map(plugin => ({
95
- tabTitle: plugin.tabTitle
96
- }));
97
- }
58
+ return { ...state,
59
+ ...payload
60
+ };
61
+ }
98
62
 
99
- return [];
63
+ function LinkPicker({
64
+ onSubmit,
65
+ onCancel,
66
+ plugins,
67
+ url: initUrl,
68
+ displayText: initDisplayText
69
+ }) {
70
+ const [state, dispatch] = useReducer(reducer, { ...initState,
71
+ url: normalizeUrl(initUrl) || '',
72
+ displayText: initDisplayText || ''
73
+ });
74
+ const {
75
+ activeIndex,
76
+ selectedIndex,
77
+ url,
78
+ displayText,
79
+ invalidUrl
80
+ } = state;
81
+ const isEditing = !!initUrl;
82
+ const intl = useIntl();
83
+ const {
84
+ items,
85
+ isLoading,
86
+ isSelectedItem,
87
+ isActivePlugin,
88
+ tabs
89
+ } = usePlugins(state, plugins);
90
+ const handleChangeUrl = useCallback(url => {
91
+ dispatch({
92
+ url
100
93
  });
101
-
102
- _defineProperty(this, "isUrlPopulatedWithSelectedItem", () => {
103
- /**
104
- * When we use ArrowKey to navigate through result items,
105
- * the URL field will be populated with the content of
106
- * selected item.
107
- * This function will check if the URL field is populated
108
- * with selected item.
109
- * It can be useful to detect whether we want to insert a
110
- * smartlink or a hyperlink with customized title
111
- */
112
- const {
113
- items,
114
- selectedIndex,
115
- url
116
- } = this.state;
117
- const selectedItem = items[selectedIndex];
118
-
119
- if (selectedItem && selectedItem.url === url) {
120
- return true;
121
- }
122
-
123
- return false;
94
+ }, []);
95
+ const handleChangeText = useCallback(displayText => {
96
+ dispatch({
97
+ displayText
124
98
  });
99
+ }, []);
100
+ const handleKeyDown = useCallback(event => {
101
+ const {
102
+ keyCode
103
+ } = event;
104
+ const KEY_CODE_ARROW_DOWN = 40;
105
+ const KEY_CODE_ARROW_UP = 38;
125
106
 
126
- _defineProperty(this, "handleSelected", objectId => {
127
- const {
128
- items
129
- } = this.state;
130
- const selectedItem = items.find(item => item.objectId === objectId);
107
+ if (!items || !items.length) {
108
+ return;
109
+ }
131
110
 
132
- if (selectedItem) {
133
- const {
134
- url,
135
- name
136
- } = selectedItem;
137
- this.handleInsert(url, name, 'typeAhead');
138
- }
139
- });
111
+ let updatedIndex = activeIndex;
140
112
 
141
- _defineProperty(this, "handleInsert", (url, title, inputType) => {
142
- const {
143
- onSubmit
144
- } = this.props;
145
- const {
146
- url: rawUrl,
147
- displayText
148
- } = this.state;
113
+ switch (keyCode) {
114
+ case KEY_CODE_ARROW_DOWN:
115
+ // down
116
+ event.preventDefault();
117
+ updatedIndex = (activeIndex + 1) % items.length;
118
+ break;
149
119
 
150
- if (onSubmit) {
151
- onSubmit({
152
- url,
153
- displayText: displayText || null,
154
- title: title || null,
155
- meta: {
156
- inputMethod: inputType
157
- },
158
- ...(inputType === 'manual' ? {
159
- rawUrl
160
- } : {})
161
- });
162
- }
163
- });
120
+ case KEY_CODE_ARROW_UP:
121
+ // up
122
+ event.preventDefault();
123
+ updatedIndex = activeIndex > 0 ? activeIndex - 1 : items.length - 1;
124
+ break;
125
+ }
164
126
 
165
- _defineProperty(this, "handleMouseEnterResultItem", objectId => {
166
- const {
167
- items
168
- } = this.state;
169
- const index = findIndex(items, item => item.objectId === objectId);
170
- this.setState({
171
- activeIndex: index
127
+ if ([KEY_CODE_ARROW_DOWN, KEY_CODE_ARROW_UP].includes(keyCode) && items[updatedIndex]) {
128
+ dispatch({
129
+ activeIndex: updatedIndex,
130
+ selectedIndex: updatedIndex,
131
+ url: items[updatedIndex].url,
132
+ invalidUrl: false
172
133
  });
134
+ }
135
+ }, [activeIndex, items]);
136
+ const handleClear = useCallback(field => {
137
+ dispatch({
138
+ [field]: ''
173
139
  });
174
-
175
- _defineProperty(this, "handleMouseLeaveResultItem", objectId => {
176
- const {
177
- items,
178
- activeIndex
179
- } = this.state;
180
- const index = findIndex(items, item => item.objectId === objectId); // This is to avoid updating index that was set by other mouseenter event
181
-
182
- if (activeIndex === index) {
183
- this.setState({
184
- activeIndex: -1
185
- });
186
- }
140
+ }, []);
141
+ const handleMouseEnterResultItem = useCallback(objectId => {
142
+ const index = findIndex(items, item => item.objectId === objectId);
143
+ dispatch({
144
+ activeIndex: index
187
145
  });
146
+ }, [items]);
147
+ const handleMouseLeaveResultItem = useCallback(objectId => {
148
+ const index = findIndex(items, item => item.objectId === objectId); // This is to avoid updating index that was set by other mouseenter event
188
149
 
189
- _defineProperty(this, "handleSubmit", () => {
190
- const {
191
- url,
192
- selectedIndex,
193
- items
194
- } = this.state;
195
- const {
196
- intl
197
- } = this.props;
198
- const selectedItem = items[selectedIndex];
199
-
200
- if (this.isUrlPopulatedWithSelectedItem()) {
201
- return this.handleInsert(selectedItem.url, selectedItem.name, 'typeAhead');
202
- }
203
-
204
- const normalized = normalizeUrl(url);
205
-
206
- if (normalized) {
207
- return this.handleInsert(normalized, null, 'manual');
208
- }
209
-
210
- return this.setState({
211
- invalidUrl: intl.formatMessage(messages.linkInvalid)
150
+ if (activeIndex === index) {
151
+ dispatch({
152
+ activeIndex: -1
212
153
  });
154
+ }
155
+ }, [activeIndex, items]);
156
+ const handleInsert = useCallback((url, title, inputType) => {
157
+ onSubmit({
158
+ url,
159
+ displayText: displayText || null,
160
+ title: title || null,
161
+ meta: {
162
+ inputMethod: inputType
163
+ },
164
+ ...(inputType === 'manual' ? {
165
+ rawUrl: state.url
166
+ } : {})
213
167
  });
168
+ }, [displayText, onSubmit, state.url]);
169
+ const handleSelected = useCallback(objectId => {
170
+ const selectedItem = items.find(item => item.objectId === objectId);
214
171
 
215
- _defineProperty(this, "handleKeyDown", event => {
216
- const {
217
- items,
218
- activeIndex
219
- } = this.state;
220
- const {
221
- keyCode
222
- } = event;
223
- const KEY_CODE_ARROW_DOWN = 40;
224
- const KEY_CODE_ARROW_UP = 38;
225
-
226
- if (!items || !items.length) {
227
- return;
228
- }
229
-
230
- let updatedIndex = activeIndex;
231
-
232
- switch (keyCode) {
233
- case KEY_CODE_ARROW_DOWN:
234
- // down
235
- event.preventDefault();
236
- updatedIndex = (activeIndex + 1) % items.length;
237
- break;
238
-
239
- case KEY_CODE_ARROW_UP:
240
- // up
241
- event.preventDefault();
242
- updatedIndex = activeIndex > 0 ? activeIndex - 1 : items.length - 1;
243
- break;
244
- }
245
-
246
- if ([KEY_CODE_ARROW_DOWN, KEY_CODE_ARROW_UP].includes(keyCode) && items[updatedIndex]) {
247
- this.setState({
248
- activeIndex: updatedIndex,
249
- selectedIndex: updatedIndex,
250
- url: items[updatedIndex].url,
251
- invalidUrl: null
252
- });
253
- }
254
- });
255
-
256
- _defineProperty(this, "handleCancel", () => {
172
+ if (selectedItem) {
257
173
  const {
258
- onCancel
259
- } = this.props;
260
-
261
- if (onCancel) {
262
- onCancel();
263
- }
264
- });
265
-
266
- _defineProperty(this, "updateTextInput", displayText => {
267
- this.setState({
268
- displayText: displayText
269
- });
270
- });
271
-
272
- this.state = {
273
- selectedIndex: -1,
274
- activeIndex: -1,
275
- isLoading: false,
276
- isEditing: !!props.url,
277
- url: normalizeUrl(props.url) || '',
278
- displayText: props.displayText || '',
279
- items: [],
280
- invalidUrl: null,
281
- activePlugin: 0
282
- };
283
- /* Cache functions */
284
-
285
- this.handleClearUrl = this.createClearHandler('url');
286
- this.handleClearDisplayText = this.createClearHandler('displayText');
287
- }
288
-
289
- componentDidMount() {
290
- this.loadInitialLinkSearchResult();
291
- }
292
-
293
- loadInitialLinkSearchResult() {
294
- const {
295
- url
296
- } = this.state;
297
-
298
- if (!url) {
299
- this.updateResults();
174
+ url,
175
+ name
176
+ } = selectedItem;
177
+ handleInsert(url, name, 'typeAhead');
300
178
  }
301
- }
179
+ }, [handleInsert, items]);
180
+ const handleSubmit = useCallback(() => {
181
+ const selectedItem = items[selectedIndex];
302
182
 
303
- getActivePlugin() {
304
- if (!this.props.plugins || this.props.plugins.length === 0) {
305
- return null;
183
+ if (isSelectedItem) {
184
+ return handleInsert(selectedItem.url, selectedItem.name, 'typeAhead');
306
185
  }
307
186
 
308
- return this.props.plugins[this.state.activePlugin];
309
- }
310
- /**
311
- * Interface between link picker and plugins to update results
312
- */
313
-
314
-
315
- async updateResults() {
316
- const queryVersion = ++this.queryVersion;
317
- const activePlugin = this.getActivePlugin();
187
+ const normalized = normalizeUrl(url);
318
188
 
319
- if (!activePlugin) {
320
- return;
189
+ if (normalized) {
190
+ return handleInsert(normalized, null, 'manual');
321
191
  }
322
192
 
323
- const updates = activePlugin.resolve({
324
- query: this.state.url
325
- });
326
- this.setState({
327
- isLoading: true,
328
- items: []
193
+ return dispatch({
194
+ invalidUrl: true
329
195
  });
196
+ }, [handleInsert, isSelectedItem, items, selectedIndex, url]);
197
+ const linkPlaceHolder = isActivePlugin ? messages.placeholder : messages.linkPlaceholder;
198
+ const linkListTitle = url ? messages.titleResults : messages.titleRecentlyViewed;
199
+ const insertButtonMsg = isEditing ? messages.saveButton : messages.insertButton;
200
+ const noResults = isActivePlugin && !isLoading && !items.length && !isSafeUrl(url);
201
+ const screenReaderDescriptionId = 'search-recent-links-field-description';
202
+ const linkSearchListId = 'link-picker-search-list';
203
+ const ariaActiveDescendant = selectedIndex > -1 ? `link-search-list-item-${selectedIndex}` : ''; // Added workaround with a screen reader Announcer specifically for VoiceOver + Safari
204
+ // as the Aria design pattern for combobox does not work in this case
205
+ // for details: https://a11y-internal.atlassian.net/browse/AK-740
206
+
207
+ const screenReaderText = browser.safari && getScreenReaderText(items, selectedIndex, intl);
208
+ const searchIcon = isActivePlugin && jsx("span", {
209
+ css: searchIconStyles,
210
+ "data-testid": testIds.searchIcon
211
+ }, jsx(EditorSearchIcon, {
212
+ size: "medium",
213
+ label: ''
214
+ }));
215
+ return jsx("div", {
216
+ "data-testid": testIds.linkPicker,
217
+ css: rootContainerStyles
218
+ }, screenReaderText && jsx(Announcer, {
219
+ ariaLive: "assertive",
220
+ text: screenReaderText,
221
+ ariaRelevant: "additions",
222
+ delay: 250
223
+ }), jsx(VisuallyHidden, {
224
+ id: screenReaderDescriptionId
225
+ }, jsx(FormattedMessage, messages.searchLinkAriaDescription)), jsx(PanelTextInput, {
226
+ role: "combobox",
227
+ autoComplete: "off",
228
+ name: "url",
229
+ testId: testIds.urlInputField,
230
+ label: intl.formatMessage(messages.linkLabel),
231
+ placeholder: intl.formatMessage(linkPlaceHolder),
232
+ value: url,
233
+ autoFocus: true,
234
+ elemBeforeInput: searchIcon,
235
+ clearLabel: intl.formatMessage(messages.clearLink),
236
+ "aria-expanded": true,
237
+ "aria-autocomplete": "list",
238
+ "aria-controls": linkSearchListId,
239
+ "aria-activedescendant": ariaActiveDescendant,
240
+ "aria-describedby": screenReaderDescriptionId,
241
+ error: invalidUrl ? intl.formatMessage(messages.linkInvalid) : null,
242
+ onClear: handleClear,
243
+ onSubmit: handleSubmit,
244
+ onKeyDown: handleKeyDown,
245
+ onChange: handleChangeUrl
246
+ }), jsx(PanelTextInput, {
247
+ autoComplete: "off",
248
+ name: "displayText",
249
+ testId: testIds.textInputField,
250
+ value: displayText,
251
+ label: intl.formatMessage(messages.linkTextLabel),
252
+ placeholder: intl.formatMessage(messages.linkTextPlaceholder),
253
+ clearLabel: intl.formatMessage(messages.clearText),
254
+ "aria-label": intl.formatMessage(messages.linkAriaLabel),
255
+ onClear: handleClear,
256
+ onSubmit: handleSubmit,
257
+ onKeyDown: handleKeyDown,
258
+ onChange: handleChangeText
259
+ }), tabs.length > 0 && jsx(Tabs, {
260
+ id: testIds.tabList,
261
+ testId: testIds.tabList,
262
+ onChange: activeTab => dispatch({
263
+ selectedIndex: -1,
264
+ activeIndex: -1,
265
+ invalidUrl: false,
266
+ activeTab
267
+ })
268
+ }, jsx(TabList, null, tabs.map(tab => jsx(Tab, {
269
+ key: tab.tabTitle,
270
+ testId: testIds.tabItem
271
+ }, tab.tabTitle)))), !!items.length && jsx("div", {
272
+ css: listTitleStyles,
273
+ id: testIds.resultListTitle,
274
+ "data-testid": testIds.resultListTitle
275
+ }, jsx(FormattedMessage, linkListTitle)), jsx(VisuallyHidden, {
276
+ id: "fabric.smartcard.linkpicker.suggested.results"
277
+ }, url && jsx(FormattedMessage, _extends({}, messages.searchLinkResults, {
278
+ values: {
279
+ count: items.length
280
+ },
281
+ "aria-live": "polite",
282
+ "aria-atomic": "true"
283
+ }))), jsx(LinkSearchList, {
284
+ ariaLabelledBy: testIds.resultListTitle,
285
+ ariaControls: "fabric.smartcard.linkpicker.suggested.results",
286
+ id: linkSearchListId,
287
+ role: "listbox",
288
+ items: items,
289
+ isLoading: isLoading,
290
+ selectedIndex: selectedIndex,
291
+ activeIndex: activeIndex,
292
+ onSelect: handleSelected,
293
+ onMouseEnter: handleMouseEnterResultItem,
294
+ onMouseLeave: handleMouseLeaveResultItem
295
+ }), noResults && jsx(LinkSearchNoResults, {
296
+ testId: testIds.emptyResultPage,
297
+ header: intl.formatMessage(messages.noResults),
298
+ description: intl.formatMessage(messages.noResultsDescription)
299
+ }), jsx(FormFooter, {
300
+ align: "end"
301
+ }, jsx(ButtonGroup, null, jsx(Button, {
302
+ appearance: "default",
303
+ onClick: onCancel,
304
+ testId: testIds.cancelButton
305
+ }, intl.formatMessage(messages.cancelButton)), jsx(Button, {
306
+ type: "submit",
307
+ appearance: "primary",
308
+ onClick: handleSubmit,
309
+ testId: testIds.insertButton
310
+ }, intl.formatMessage(insertButtonMsg)))));
311
+ }
330
312
 
331
- try {
332
- while (queryVersion === this.queryVersion) {
333
- let done = false;
334
- let value;
335
-
336
- if (updates instanceof Promise) {
337
- done = true;
338
- value = await updates;
339
- } else {
340
- var _result$done;
341
-
342
- const result = await updates.next();
343
- done = (_result$done = result.done) !== null && _result$done !== void 0 ? _result$done : false;
344
- value = result.value;
345
- }
346
-
347
- if (queryVersion !== this.queryVersion) {
348
- return;
349
- }
350
-
351
- this.setState(prev => ({
352
- items: limit(value.data),
353
- isLoading: !done
354
- }));
355
-
356
- if (done) {
357
- return;
358
- }
359
- }
360
- } catch {
361
- this.setState({
362
- isLoading: false
363
- });
364
- }
365
- }
313
+ export default LinkPicker;
366
314
 
367
- render() {
315
+ function getScreenReaderText(items, selectedIndex, intl) {
316
+ if (items.length && selectedIndex > -1) {
368
317
  const {
369
- items,
370
- isLoading,
371
- isEditing,
372
- activeIndex,
373
- selectedIndex,
374
- url,
375
- displayText
376
- } = this.state;
377
- const {
378
- intl: {
379
- formatMessage
380
- }
381
- } = this.props;
382
- const activePlugin = this.getActivePlugin();
383
- const tabs = this.getTabs();
384
- const linkPlaceHolder = formatMessage(activePlugin ? messages.placeholder : messages.linkPlaceholder);
385
- const linkLabel = formatMessage(messages.linkLabel);
386
- const linkTextLabel = formatMessage(messages.linkTextLabel);
387
- const linkTextPlaceHolder = formatMessage(messages.linkTextPlaceholder);
388
- const screenReaderDescriptionId = 'search-recent-links-field-description';
389
- const linkSearchListId = 'link-picker-search-list';
390
- const ariaActiveDescendant = selectedIndex > -1 ? `link-search-list-item-${selectedIndex}` : ''; // Added workaround with a screen reader Announcer specifically for VoiceOver + Safari
391
- // as the Aria design pattern for combobox does not work in this case
392
- // for details: https://a11y-internal.atlassian.net/browse/AK-740
393
-
394
- const screenReaderText = browser.safari && this.getScreenReaderText();
395
- const searchIcon = activePlugin && jsx("span", {
396
- css: searchIconStyles,
397
- "data-testid": "link-picker-search-icon"
398
- }, jsx(EditorSearchIcon, {
399
- size: "medium",
400
- label: ''
401
- }));
402
- const noResults = activePlugin && !isLoading && !items.length && !isSafeUrl(url);
403
- return jsx("div", {
404
- "data-testid": "link-picker",
405
- css: rootContainerStyles
406
- }, screenReaderText && jsx(Announcer, {
407
- ariaLive: "assertive",
408
- text: screenReaderText,
409
- ariaRelevant: "additions",
410
- delay: 250
411
- }), jsx(VisuallyHidden, null, jsx("span", {
412
- "aria-hidden": "true",
413
- id: screenReaderDescriptionId
414
- }, formatMessage(messages.searchLinkAriaDescription))), jsx(PanelTextInput, {
415
- name: "link",
416
- testId: "link-url",
417
- role: "combobox",
418
- label: linkLabel,
419
- placeholder: linkPlaceHolder,
420
- value: url,
421
- autoFocus: true,
422
- autoComplete: "off",
423
- elemBeforeInput: searchIcon,
424
- onSubmit: this.handleSubmit,
425
- onChange: this.updateInput,
426
- onKeyDown: this.handleKeyDown,
427
- onClear: this.handleClearUrl,
428
- error: this.state.invalidUrl,
429
- clearLabel: formatMessage(messages.clearLink),
430
- "aria-controls": linkSearchListId,
431
- "aria-autocomplete": "list",
432
- "aria-activedescendant": ariaActiveDescendant,
433
- "aria-describedby": screenReaderDescriptionId,
434
- "aria-expanded": true
435
- }), jsx(PanelTextInput, {
436
- name: "link-text",
437
- testId: "link-text",
438
- autoComplete: "off",
439
- label: linkTextLabel,
440
- placeholder: linkTextPlaceHolder,
441
- "aria-label": formatMessage(messages.linkAriaLabel),
442
- value: displayText,
443
- onSubmit: this.handleSubmit,
444
- onKeyDown: this.handleKeyDown,
445
- onChange: this.updateTextInput,
446
- onClear: this.handleClearDisplayText,
447
- clearLabel: formatMessage(messages.clearText)
448
- }), tabs.length > 0 && jsx(Tabs, {
449
- id: "link-picker-tabs",
450
- testId: "link-picker-tabs",
451
- onChange: activePlugin => this.setState({
452
- activePlugin
453
- }, () => {
454
- this.updateResults();
455
- })
456
- }, jsx(TabList, null, tabs.map(tab => {
457
- const label = tab.tabTitle;
458
- return jsx(Tab, {
459
- key: label,
460
- testId: "link-picker-tab"
461
- }, label);
462
- }))), !!items.length && jsx("div", {
463
- css: listTitleStyles,
464
- id: "link-picker-list-title",
465
- "data-testid": "link-picker-list-title"
466
- }, formatMessage(this.state.url ? messages.titleResults : messages.titleRecentlyViewed)), jsx(VisuallyHidden, null, jsx("span", {
467
- "aria-live": "polite",
468
- "aria-atomic": "true",
469
- id: "fabric.smartcard.linkpicker.suggested.results"
470
- }, url && formatMessage(messages.searchLinkResults, {
471
- count: items.length
472
- }))), jsx(LinkSearchList, {
473
- ariaLabelledBy: "link-picker-list-title",
474
- ariaControls: "fabric.smartcard.linkpicker.suggested.results",
475
- id: linkSearchListId,
476
- role: "listbox",
477
- items: items,
478
- isLoading: isLoading,
479
- selectedIndex: selectedIndex,
480
- activeIndex: activeIndex,
481
- onSelect: this.handleSelected,
482
- onMouseEnter: this.handleMouseEnterResultItem,
483
- onMouseLeave: this.handleMouseLeaveResultItem
484
- }), noResults && jsx(LinkSearchNoResults, {
485
- testId: "link-search-no-results",
486
- header: formatMessage(messages.noResults),
487
- description: formatMessage(messages.noResultsDescription)
488
- }), jsx(FormFooter, {
489
- align: "end"
490
- }, jsx(ButtonGroup, null, jsx(Button, {
491
- appearance: "default",
492
- onClick: this.handleCancel,
493
- testId: "link-picker-cancel-button"
494
- }, formatMessage(messages.cancelButton)), jsx(Button, {
495
- type: "submit",
496
- appearance: "primary",
497
- onClick: this.handleSubmit,
498
- testId: "link-picker-insert-button"
499
- }, formatMessage(isEditing ? messages.saveButton : messages.insertButton)))));
318
+ name,
319
+ container,
320
+ lastUpdatedDate,
321
+ lastViewedDate
322
+ } = items[selectedIndex];
323
+ const date = transformTimeStamp(intl, lastViewedDate, lastUpdatedDate);
324
+ 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(' ');
325
+ return [name, container, formattedDate].filter(Boolean).join(', ');
500
326
  }
501
-
502
327
  }
503
328
 
504
329
  function findIndex(array, predicate) {
@@ -512,10 +337,4 @@ function findIndex(array, predicate) {
512
337
  return false;
513
338
  });
514
339
  return index;
515
- }
516
-
517
- function limit(items) {
518
- return items.slice(0, RECENT_SEARCH_LIST_SIZE);
519
- }
520
-
521
- export default injectIntl(LinkPicker);
340
+ }