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