@atlaskit/link-picker 1.8.3 → 1.9.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.
- package/CHANGELOG.md +18 -0
- package/dist/cjs/controllers/use-fix-height/index.js +27 -0
- package/dist/cjs/services/use-plugins/index.js +48 -90
- package/dist/cjs/services/use-plugins/reducer.js +64 -0
- package/dist/cjs/services/use-plugins/utils.js +134 -0
- package/dist/cjs/ui/index.js +2 -1
- package/dist/cjs/ui/link-picker/form-footer/index.js +76 -0
- package/dist/cjs/ui/link-picker/form-footer/styled.js +17 -0
- package/dist/cjs/ui/link-picker/index.js +35 -76
- package/dist/cjs/ui/link-picker/link-search-error/index.js +47 -6
- package/dist/cjs/ui/link-picker/link-search-list/index.js +89 -20
- package/dist/cjs/ui/link-picker/link-search-list/link-search-no-results/index.js +54 -0
- package/dist/cjs/ui/link-picker/{link-search-no-results → link-search-list/link-search-no-results}/no-results-svg/index.js +0 -0
- package/dist/cjs/ui/link-picker/{link-search-no-results → link-search-list/link-search-no-results}/no-results-svg/styled.js +0 -0
- package/dist/cjs/ui/link-picker/link-search-list/link-search-no-results/styled.js +17 -0
- package/dist/cjs/ui/link-picker/link-search-list/styled.js +13 -6
- package/dist/cjs/ui/link-picker/messages.js +0 -50
- package/dist/cjs/ui/link-picker/styled.js +16 -11
- package/dist/cjs/ui/link-picker/text-input/styled.js +1 -1
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/controllers/use-fix-height/index.js +19 -0
- package/dist/es2019/services/use-plugins/index.js +35 -57
- package/dist/es2019/services/use-plugins/reducer.js +37 -0
- package/dist/es2019/services/use-plugins/utils.js +59 -0
- package/dist/es2019/ui/index.js +1 -1
- package/dist/es2019/ui/link-picker/form-footer/index.js +51 -0
- package/dist/es2019/ui/link-picker/form-footer/styled.js +5 -0
- package/dist/es2019/ui/link-picker/index.js +30 -68
- package/dist/es2019/ui/link-picker/link-search-error/index.js +40 -4
- package/dist/es2019/ui/link-picker/link-search-list/index.js +60 -14
- package/dist/es2019/ui/link-picker/link-search-list/link-search-no-results/index.js +35 -0
- package/dist/es2019/ui/link-picker/{link-search-no-results → link-search-list/link-search-no-results}/no-results-svg/index.js +0 -0
- package/dist/es2019/ui/link-picker/{link-search-no-results → link-search-list/link-search-no-results}/no-results-svg/styled.js +0 -0
- package/dist/es2019/ui/link-picker/link-search-list/link-search-no-results/styled.js +6 -0
- package/dist/es2019/ui/link-picker/link-search-list/styled.js +23 -2
- package/dist/es2019/ui/link-picker/messages.js +0 -50
- package/dist/es2019/ui/link-picker/styled.js +20 -15
- package/dist/es2019/ui/link-picker/text-input/styled.js +3 -1
- package/dist/es2019/version.json +1 -1
- package/dist/esm/controllers/use-fix-height/index.js +19 -0
- package/dist/esm/services/use-plugins/index.js +49 -94
- package/dist/esm/services/use-plugins/reducer.js +43 -0
- package/dist/esm/services/use-plugins/utils.js +111 -0
- package/dist/esm/ui/index.js +1 -1
- package/dist/esm/ui/link-picker/form-footer/index.js +53 -0
- package/dist/esm/ui/link-picker/form-footer/styled.js +6 -0
- package/dist/esm/ui/link-picker/index.js +31 -73
- package/dist/esm/ui/link-picker/link-search-error/index.js +41 -5
- package/dist/esm/ui/link-picker/link-search-list/index.js +67 -13
- package/dist/esm/ui/link-picker/link-search-list/link-search-no-results/index.js +37 -0
- package/dist/esm/ui/link-picker/{link-search-no-results → link-search-list/link-search-no-results}/no-results-svg/index.js +0 -0
- package/dist/esm/ui/link-picker/{link-search-no-results → link-search-list/link-search-no-results}/no-results-svg/styled.js +0 -0
- package/dist/esm/ui/link-picker/link-search-list/link-search-no-results/styled.js +6 -0
- package/dist/esm/ui/link-picker/link-search-list/styled.js +11 -5
- package/dist/esm/ui/link-picker/messages.js +0 -50
- package/dist/esm/ui/link-picker/styled.js +14 -11
- package/dist/esm/ui/link-picker/text-input/styled.js +1 -1
- package/dist/esm/version.json +1 -1
- package/dist/types/controllers/use-fix-height/index.d.ts +8 -0
- package/dist/types/services/use-plugins/reducer.d.ts +30 -0
- package/dist/types/services/use-plugins/utils.d.ts +18 -0
- package/dist/types/ui/index.d.ts +5 -0
- package/dist/types/ui/link-picker/form-footer/index.d.ts +13 -0
- package/dist/types/ui/link-picker/form-footer/styled.d.ts +1 -0
- package/dist/types/ui/link-picker/index.d.ts +21 -18
- package/dist/types/ui/link-picker/link-search-error/index.d.ts +19 -3
- package/dist/types/ui/link-picker/link-search-list/index.d.ts +30 -5
- package/dist/types/ui/link-picker/link-search-list/link-search-no-results/index.d.ts +19 -0
- package/dist/types/ui/link-picker/{link-search-no-results → link-search-list/link-search-no-results}/no-results-svg/index.d.ts +0 -0
- package/dist/types/ui/link-picker/{link-search-no-results → link-search-list/link-search-no-results}/no-results-svg/styled.d.ts +0 -0
- package/dist/types/ui/link-picker/link-search-list/link-search-no-results/styled.d.ts +1 -0
- package/dist/types/ui/link-picker/link-search-list/styled.d.ts +1 -0
- package/dist/types/ui/link-picker/messages.d.ts +0 -50
- package/dist/types/ui/link-picker/styled.d.ts +5 -1
- package/package.json +15 -14
- package/report.api.md +8 -2
- package/dist/cjs/ui/link-picker/link-search-no-results/index.js +0 -27
- package/dist/es2019/ui/link-picker/link-search-no-results/index.js +0 -10
- package/dist/esm/ui/link-picker/link-search-no-results/index.js +0 -14
- package/dist/types/ui/link-picker/link-search-no-results/index.d.ts +0 -4
|
@@ -21,5 +21,5 @@ var _templateObject, _templateObject2;
|
|
|
21
21
|
|
|
22
22
|
var clearTextButtonStyles = (0, _react.css)(_templateObject || (_templateObject = (0, _taggedTemplateLiteral2.default)(["\n padding: 0;\n margin-right: ", "px;\n color: ", ";\n background: transparent;\n border: none;\n cursor: pointer;\n"])), (0, _constants.gridSize)() / 2, (0, _tokens.token)('color.icon.subtle', _colors.N500));
|
|
23
23
|
exports.clearTextButtonStyles = clearTextButtonStyles;
|
|
24
|
-
var fieldStyles = (0, _react.css)(_templateObject2 || (_templateObject2 = (0, _taggedTemplateLiteral2.default)(["\n margin-
|
|
24
|
+
var fieldStyles = (0, _react.css)(_templateObject2 || (_templateObject2 = (0, _taggedTemplateLiteral2.default)(["\n & + & {\n margin-top: ", "px;\n }\n"])), (0, _constants.gridSize)() * 2);
|
|
25
25
|
exports.fieldStyles = fieldStyles;
|
package/dist/cjs/version.json
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useLayoutEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
const useFixHeight = shouldFixHeight => {
|
|
4
|
+
const ref = useRef(null);
|
|
5
|
+
const currentHeight = useRef(null);
|
|
6
|
+
useLayoutEffect(() => {
|
|
7
|
+
if (ref.current) {
|
|
8
|
+
currentHeight.current = ref.current.getBoundingClientRect().height;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
return {
|
|
12
|
+
ref,
|
|
13
|
+
style: shouldFixHeight ? {
|
|
14
|
+
minHeight: currentHeight.current || 'auto'
|
|
15
|
+
} : undefined
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default useFixHeight;
|
|
@@ -1,25 +1,10 @@
|
|
|
1
|
-
import { useEffect,
|
|
1
|
+
import { useEffect, useMemo, useState, useCallback } from 'react';
|
|
2
2
|
import { RECENT_SEARCH_LIST_SIZE } from '../../ui/link-picker';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
return { ...state,
|
|
6
|
-
...payload
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
|
-
|
|
3
|
+
import { CancellationError, resolvePluginUpdates } from './utils';
|
|
4
|
+
import { usePluginReducer } from './reducer';
|
|
10
5
|
export function usePlugins(state, activeTab, plugins) {
|
|
11
6
|
const [retries, setRetries] = useState(0);
|
|
12
|
-
const [pluginState, dispatch] =
|
|
13
|
-
items: null,
|
|
14
|
-
error: null,
|
|
15
|
-
isLoading: false
|
|
16
|
-
});
|
|
17
|
-
const {
|
|
18
|
-
items,
|
|
19
|
-
isLoading,
|
|
20
|
-
error
|
|
21
|
-
} = pluginState;
|
|
22
|
-
const queryVersion = useRef(0);
|
|
7
|
+
const [pluginState, dispatch] = usePluginReducer();
|
|
23
8
|
const activePlugin = plugins === null || plugins === void 0 ? void 0 : plugins[activeTab];
|
|
24
9
|
useEffect(() => {
|
|
25
10
|
if (!activePlugin) {
|
|
@@ -28,62 +13,50 @@ export function usePlugins(state, activeTab, plugins) {
|
|
|
28
13
|
|
|
29
14
|
if (!state) {
|
|
30
15
|
dispatch({
|
|
31
|
-
|
|
32
|
-
isLoading: false,
|
|
33
|
-
error: null
|
|
16
|
+
type: 'CLEAR'
|
|
34
17
|
});
|
|
35
18
|
return;
|
|
36
19
|
}
|
|
37
20
|
|
|
38
|
-
const updates = activePlugin.resolve(state);
|
|
39
21
|
dispatch({
|
|
40
|
-
|
|
41
|
-
isLoading: true,
|
|
42
|
-
error: null
|
|
22
|
+
type: 'LOADING'
|
|
43
23
|
});
|
|
44
|
-
const
|
|
24
|
+
const {
|
|
25
|
+
next,
|
|
26
|
+
cancel
|
|
27
|
+
} = resolvePluginUpdates(activePlugin, state);
|
|
45
28
|
|
|
46
29
|
const updateResults = async () => {
|
|
47
30
|
try {
|
|
48
|
-
|
|
49
|
-
let done = false;
|
|
50
|
-
let value;
|
|
51
|
-
|
|
52
|
-
if (updates instanceof Promise) {
|
|
53
|
-
done = true;
|
|
54
|
-
value = await updates;
|
|
55
|
-
} else {
|
|
56
|
-
var _result$done;
|
|
57
|
-
|
|
58
|
-
const result = await updates.next();
|
|
59
|
-
done = (_result$done = result.done) !== null && _result$done !== void 0 ? _result$done : false;
|
|
60
|
-
value = result.value;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (newQuery !== queryVersion.current) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
31
|
+
let isLoading = true;
|
|
66
32
|
|
|
33
|
+
while (isLoading) {
|
|
34
|
+
const {
|
|
35
|
+
value,
|
|
36
|
+
done
|
|
37
|
+
} = await next();
|
|
38
|
+
isLoading = !done;
|
|
67
39
|
dispatch({
|
|
68
|
-
|
|
69
|
-
|
|
40
|
+
type: 'SUCCESS',
|
|
41
|
+
payload: {
|
|
42
|
+
items: limit(value.data),
|
|
43
|
+
isLoading: !done
|
|
44
|
+
}
|
|
70
45
|
});
|
|
71
|
-
|
|
72
|
-
if (done) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
46
|
}
|
|
76
47
|
} catch (error) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
48
|
+
if (!(error instanceof CancellationError)) {
|
|
49
|
+
dispatch({
|
|
50
|
+
type: 'ERROR',
|
|
51
|
+
payload: error
|
|
52
|
+
});
|
|
53
|
+
}
|
|
82
54
|
}
|
|
83
55
|
};
|
|
84
56
|
|
|
85
57
|
updateResults();
|
|
86
|
-
|
|
58
|
+
return cancel;
|
|
59
|
+
}, [activePlugin, state, retries, dispatch]);
|
|
87
60
|
const tabs = useMemo(() => {
|
|
88
61
|
if (!plugins || plugins.length <= 1) {
|
|
89
62
|
return [];
|
|
@@ -96,6 +69,11 @@ export function usePlugins(state, activeTab, plugins) {
|
|
|
96
69
|
const handleRetry = useCallback(() => {
|
|
97
70
|
setRetries(prev => ++prev);
|
|
98
71
|
}, []);
|
|
72
|
+
const {
|
|
73
|
+
items,
|
|
74
|
+
isLoading,
|
|
75
|
+
error
|
|
76
|
+
} = pluginState;
|
|
99
77
|
return {
|
|
100
78
|
tabs,
|
|
101
79
|
items,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useReducer } from 'react';
|
|
2
|
+
export const ACTION_CLEAR = 'CLEAR';
|
|
3
|
+
export const ACTION_LOADING = 'LOADING';
|
|
4
|
+
export const ACTION_SUCCESS = 'SUCCESS';
|
|
5
|
+
export const ACTION_ERRORED = 'ERROR';
|
|
6
|
+
export const INITIAL_STATE = {
|
|
7
|
+
items: null,
|
|
8
|
+
error: null,
|
|
9
|
+
isLoading: false
|
|
10
|
+
};
|
|
11
|
+
export function reducer(state, action) {
|
|
12
|
+
switch (action.type) {
|
|
13
|
+
case ACTION_CLEAR:
|
|
14
|
+
return { ...INITIAL_STATE
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
case ACTION_LOADING:
|
|
18
|
+
return { ...INITIAL_STATE,
|
|
19
|
+
isLoading: true
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
case ACTION_SUCCESS:
|
|
23
|
+
return { ...state,
|
|
24
|
+
...action.payload,
|
|
25
|
+
error: null
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
case ACTION_ERRORED:
|
|
29
|
+
return { ...INITIAL_STATE,
|
|
30
|
+
error: action.payload
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
default:
|
|
34
|
+
return state;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export const usePluginReducer = () => useReducer(reducer, INITIAL_STATE);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export class CancellationError extends Error {}
|
|
2
|
+
export const cancellable = promise => {
|
|
3
|
+
let reject;
|
|
4
|
+
return {
|
|
5
|
+
promise: new Promise((resolve, _reject) => {
|
|
6
|
+
reject = _reject;
|
|
7
|
+
promise.then(resolve, reject);
|
|
8
|
+
}),
|
|
9
|
+
cancel: () => {
|
|
10
|
+
reject(new CancellationError('Promise cancelled'));
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Wraps the resolve with a cancellation wrapper and makes promise + generator plugin resolves more compatible
|
|
16
|
+
* Calling cancel for generator plugin prevents it from yielding any further updates
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export const resolvePluginUpdates = (plugin, state) => {
|
|
20
|
+
const updates = plugin.resolve(state); // Promise plugin
|
|
21
|
+
|
|
22
|
+
if (updates instanceof Promise) {
|
|
23
|
+
const {
|
|
24
|
+
promise,
|
|
25
|
+
cancel
|
|
26
|
+
} = cancellable(updates);
|
|
27
|
+
return {
|
|
28
|
+
cancel,
|
|
29
|
+
next: async () => ({
|
|
30
|
+
value: await promise,
|
|
31
|
+
done: true
|
|
32
|
+
})
|
|
33
|
+
};
|
|
34
|
+
} // Generator plugin
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
const cancellationCallbacks = [];
|
|
38
|
+
let cancelled = false;
|
|
39
|
+
return {
|
|
40
|
+
cancel: () => {
|
|
41
|
+
cancelled = true;
|
|
42
|
+
cancellationCallbacks.forEach(cb => cb());
|
|
43
|
+
},
|
|
44
|
+
next: () => {
|
|
45
|
+
const {
|
|
46
|
+
promise,
|
|
47
|
+
cancel
|
|
48
|
+
} = cancellable(updates.next());
|
|
49
|
+
|
|
50
|
+
if (cancelled) {
|
|
51
|
+
cancel();
|
|
52
|
+
} else {
|
|
53
|
+
cancellationCallbacks.push(cancel);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return promise;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
};
|
package/dist/es2019/ui/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { COMPONENT_NAME, ANALYTICS_CHANNEL } from '../common/constants';
|
|
|
5
5
|
import { name as packageName, version as packageVersion } from '../version.json';
|
|
6
6
|
import { ErrorBoundaryFallback } from './error-boundary-fallback';
|
|
7
7
|
import { LoaderFallback } from './loader-fallback';
|
|
8
|
-
const PACKAGE_DATA = {
|
|
8
|
+
export const PACKAGE_DATA = {
|
|
9
9
|
packageName,
|
|
10
10
|
packageVersion,
|
|
11
11
|
componentName: COMPONENT_NAME
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
|
|
3
|
+
/** @jsx jsx */
|
|
4
|
+
import { jsx } from '@emotion/react';
|
|
5
|
+
import { useIntl, defineMessages } from 'react-intl-next';
|
|
6
|
+
import Button, { ButtonGroup } from '@atlaskit/button';
|
|
7
|
+
import { formFooterStyles } from './styled';
|
|
8
|
+
import { memo } from 'react';
|
|
9
|
+
const messages = defineMessages({
|
|
10
|
+
cancelButton: {
|
|
11
|
+
id: 'fabric.linkPicker.button.cancel',
|
|
12
|
+
defaultMessage: 'Cancel',
|
|
13
|
+
description: 'Button to cancel and dismiss the link picker'
|
|
14
|
+
},
|
|
15
|
+
saveButton: {
|
|
16
|
+
id: 'fabric.linkPicker.button.save',
|
|
17
|
+
defaultMessage: 'Save',
|
|
18
|
+
description: 'Button to save edited link'
|
|
19
|
+
},
|
|
20
|
+
insertButton: {
|
|
21
|
+
id: 'fabric.linkPicker.button.insert',
|
|
22
|
+
defaultMessage: 'Insert',
|
|
23
|
+
description: 'Button to insert searched or selected link'
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
export const testIds = {
|
|
27
|
+
insertButton: 'link-picker-insert-button',
|
|
28
|
+
cancelButton: 'link-picker-cancel-button'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const FormFooter = ({
|
|
32
|
+
isEditing,
|
|
33
|
+
onCancel,
|
|
34
|
+
...restProps
|
|
35
|
+
}) => {
|
|
36
|
+
const intl = useIntl();
|
|
37
|
+
const insertButtonMsg = isEditing ? messages.saveButton : messages.insertButton;
|
|
38
|
+
return jsx("footer", _extends({
|
|
39
|
+
css: formFooterStyles
|
|
40
|
+
}, restProps), jsx(ButtonGroup, null, jsx(Button, {
|
|
41
|
+
appearance: "default",
|
|
42
|
+
onClick: onCancel,
|
|
43
|
+
testId: testIds.cancelButton
|
|
44
|
+
}, intl.formatMessage(messages.cancelButton)), jsx(Button, {
|
|
45
|
+
type: "submit",
|
|
46
|
+
appearance: "primary",
|
|
47
|
+
testId: testIds.insertButton
|
|
48
|
+
}, intl.formatMessage(insertButtonMsg))));
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default /*#__PURE__*/memo(FormFooter);
|
|
@@ -5,8 +5,6 @@ import { Fragment, useCallback, useEffect, useLayoutEffect, useReducer, memo } f
|
|
|
5
5
|
import { jsx } from '@emotion/react';
|
|
6
6
|
import { useIntl, FormattedMessage } from 'react-intl-next';
|
|
7
7
|
import { useAnalyticsEvents } from '@atlaskit/analytics-next';
|
|
8
|
-
import Button, { ButtonGroup } from '@atlaskit/button';
|
|
9
|
-
import { FormFooter } from '@atlaskit/form';
|
|
10
8
|
import EditorSearchIcon from '@atlaskit/icon/glyph/editor/search';
|
|
11
9
|
import Tabs, { Tab, TabList } from '@atlaskit/tabs';
|
|
12
10
|
import VisuallyHidden from '@atlaskit/visually-hidden';
|
|
@@ -17,14 +15,15 @@ import { useSearchQuery } from '../../services/use-search-query';
|
|
|
17
15
|
import { withLinkPickerAnalyticsContext } from '../../services/with-link-picker-analytics';
|
|
18
16
|
import { messages } from './messages';
|
|
19
17
|
import PanelTextInput from './text-input';
|
|
20
|
-
import LinkSearchList from './link-search-list';
|
|
21
18
|
import { normalizeUrl, isSafeUrl } from '../../common/utils/url';
|
|
22
|
-
import
|
|
19
|
+
import useFixHeight from '../../controllers/use-fix-height';
|
|
20
|
+
import { rootContainerStyles, searchIconStyles, tabsWrapperStyles, flexColumnStyles, formFooterMargin } from './styled';
|
|
23
21
|
import { browser } from './browser';
|
|
24
22
|
import Announcer from './announcer';
|
|
25
|
-
import LinkSearchNoResults from './link-search-no-results';
|
|
26
23
|
import { transformTimeStamp } from './transformTimeStamp';
|
|
27
|
-
import
|
|
24
|
+
import LinkSearchList, { testIds as listTestIds } from './link-search-list';
|
|
25
|
+
import LinkSearchError, { testIds as searchErrorTestIds } from './link-search-error';
|
|
26
|
+
import FormFooter, { testIds as formFooterTestIds } from './form-footer';
|
|
28
27
|
export const RECENT_SEARCH_LIST_SIZE = 5;
|
|
29
28
|
export const CONTACT_SUPPORT_LINK = 'https://support.atlassian.com/contact/';
|
|
30
29
|
export const testIds = {
|
|
@@ -32,18 +31,13 @@ export const testIds = {
|
|
|
32
31
|
urlInputField: 'link-url',
|
|
33
32
|
textInputField: 'link-text',
|
|
34
33
|
searchIcon: 'link-picker-search-icon',
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
...formFooterTestIds,
|
|
35
|
+
...searchErrorTestIds,
|
|
36
|
+
...listTestIds,
|
|
37
37
|
clearUrlButton: 'clear-text',
|
|
38
|
-
resultListTitle: 'link-picker-list-title',
|
|
39
|
-
emptyResultPage: 'link-search-no-results',
|
|
40
|
-
searchResultList: 'link-search-list',
|
|
41
|
-
searchResultItem: 'link-search-list-item',
|
|
42
|
-
searchResultLoadingIndicator: 'link-picker.results-loading-indicator',
|
|
43
38
|
urlError: 'link-error',
|
|
44
39
|
tabList: 'link-picker-tabs',
|
|
45
|
-
tabItem: 'link-picker-tab'
|
|
46
|
-
searchError: 'link-search-error'
|
|
40
|
+
tabItem: 'link-picker-tab'
|
|
47
41
|
};
|
|
48
42
|
const initState = {
|
|
49
43
|
url: '',
|
|
@@ -76,7 +70,7 @@ function LinkPicker({
|
|
|
76
70
|
url: initUrl,
|
|
77
71
|
displayText: initDisplayText
|
|
78
72
|
}) {
|
|
79
|
-
var
|
|
73
|
+
var _errorFallback;
|
|
80
74
|
|
|
81
75
|
const {
|
|
82
76
|
createAnalyticsEvent
|
|
@@ -104,6 +98,7 @@ function LinkPicker({
|
|
|
104
98
|
retry,
|
|
105
99
|
errorFallback
|
|
106
100
|
} = usePlugins(queryState, activeTab, plugins);
|
|
101
|
+
const fixListHeightProps = useFixHeight(isLoading);
|
|
107
102
|
const isEditing = !!initUrl;
|
|
108
103
|
const selectedItem = items === null || items === void 0 ? void 0 : items[selectedIndex];
|
|
109
104
|
const isSelectedItem = (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.url) === url;
|
|
@@ -189,7 +184,11 @@ function LinkPicker({
|
|
|
189
184
|
}
|
|
190
185
|
}, [activeIndex, items]);
|
|
191
186
|
const handleInsert = useCallback((url, title, inputType) => {
|
|
192
|
-
const event = createAnalyticsEvent(createEventPayload('ui.form.submitted.linkPicker', {}));
|
|
187
|
+
const event = createAnalyticsEvent(createEventPayload('ui.form.submitted.linkPicker', {})); // Clone the event so that it can be emitted for consumer usage
|
|
188
|
+
// This must happen BEFORE the original event is fired!
|
|
189
|
+
|
|
190
|
+
const consumerEvent = event.clone(); // Dispatch the original event to our channel
|
|
191
|
+
|
|
193
192
|
event.fire(ANALYTICS_CHANNEL);
|
|
194
193
|
onSubmit({
|
|
195
194
|
url,
|
|
@@ -201,7 +200,7 @@ function LinkPicker({
|
|
|
201
200
|
...(inputType === 'manual' ? {
|
|
202
201
|
rawUrl: state.url
|
|
203
202
|
} : {})
|
|
204
|
-
});
|
|
203
|
+
}, consumerEvent);
|
|
205
204
|
}, [displayText, onSubmit, state.url, createAnalyticsEvent]);
|
|
206
205
|
const handleSelected = useCallback(objectId => {
|
|
207
206
|
const selectedItem = items === null || items === void 0 ? void 0 : items.find(item => item.objectId === objectId);
|
|
@@ -233,9 +232,6 @@ function LinkPicker({
|
|
|
233
232
|
});
|
|
234
233
|
}, [handleInsert, isSelectedItem, selectedItem, url]);
|
|
235
234
|
const linkPlaceHolder = isActivePlugin ? messages.placeholder : messages.linkPlaceholder;
|
|
236
|
-
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;
|
|
237
|
-
const insertButtonMsg = isEditing ? messages.saveButton : messages.insertButton;
|
|
238
|
-
const noResults = (items === null || items === void 0 ? void 0 : items.length) === 0 && !error;
|
|
239
235
|
const screenReaderDescriptionId = 'search-recent-links-field-description';
|
|
240
236
|
const linkSearchListId = 'link-picker-search-list';
|
|
241
237
|
const ariaActiveDescendant = selectedIndex > -1 ? `link-search-list-item-${selectedIndex}` : ''; // Added workaround with a screen reader Announcer specifically for VoiceOver + Safari
|
|
@@ -293,7 +289,7 @@ function LinkPicker({
|
|
|
293
289
|
onClear: handleClear,
|
|
294
290
|
onKeyDown: handleKeyDown,
|
|
295
291
|
onChange: handleChangeText
|
|
296
|
-
}), queryState && jsx(Fragment, null, tabs.length > 0 && jsx("div", {
|
|
292
|
+
}), isActivePlugin && !!queryState && jsx(Fragment, null, tabs.length > 0 && jsx("div", {
|
|
297
293
|
css: tabsWrapperStyles
|
|
298
294
|
}, jsx(Tabs, {
|
|
299
295
|
id: testIds.tabList,
|
|
@@ -307,59 +303,25 @@ function LinkPicker({
|
|
|
307
303
|
}, jsx(TabList, null, tabs.map(tab => jsx(Tab, {
|
|
308
304
|
key: tab.tabTitle,
|
|
309
305
|
testId: testIds.tabItem
|
|
310
|
-
}, tab.tabTitle))))),
|
|
311
|
-
css:
|
|
312
|
-
|
|
313
|
-
"data-testid": testIds.resultListTitle
|
|
314
|
-
}, jsx(FormattedMessage, linkListTitle)), jsx(VisuallyHidden, {
|
|
315
|
-
id: "fabric.smartcard.linkpicker.suggested.results"
|
|
316
|
-
}, url && jsx(FormattedMessage, _extends({}, messages.searchLinkResults, {
|
|
317
|
-
values: {
|
|
318
|
-
count: (_items$length = items === null || items === void 0 ? void 0 : items.length) !== null && _items$length !== void 0 ? _items$length : 0
|
|
319
|
-
},
|
|
320
|
-
"aria-live": "polite",
|
|
321
|
-
"aria-atomic": "true"
|
|
322
|
-
}))), jsx(LinkSearchList, {
|
|
323
|
-
ariaLabelledBy: testIds.resultListTitle,
|
|
324
|
-
ariaControls: "fabric.smartcard.linkpicker.suggested.results",
|
|
306
|
+
}, tab.tabTitle))))), jsx("div", _extends({
|
|
307
|
+
css: flexColumnStyles
|
|
308
|
+
}, fixListHeightProps), !error && jsx(LinkSearchList, {
|
|
325
309
|
id: linkSearchListId,
|
|
326
310
|
role: "listbox",
|
|
327
|
-
items: items
|
|
311
|
+
items: items,
|
|
328
312
|
isLoading: isLoading,
|
|
329
313
|
selectedIndex: selectedIndex,
|
|
330
314
|
activeIndex: activeIndex,
|
|
331
315
|
onSelect: handleSelected,
|
|
332
316
|
onMouseEnter: handleMouseEnterResultItem,
|
|
333
|
-
onMouseLeave: handleMouseLeaveResultItem
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
spacing: "none",
|
|
342
|
-
href: CONTACT_SUPPORT_LINK,
|
|
343
|
-
target: "_blank",
|
|
344
|
-
rel: "noopener noreferrer"
|
|
345
|
-
}, label)
|
|
346
|
-
}
|
|
347
|
-
}))
|
|
348
|
-
})), noResults && jsx(LinkSearchNoResults, {
|
|
349
|
-
testId: testIds.emptyResultPage,
|
|
350
|
-
header: intl.formatMessage(messages.noResults),
|
|
351
|
-
description: intl.formatMessage(messages.noResultsDescription)
|
|
352
|
-
})), jsx(FormFooter, {
|
|
353
|
-
align: "end"
|
|
354
|
-
}, jsx(ButtonGroup, null, jsx(Button, {
|
|
355
|
-
appearance: "default",
|
|
356
|
-
onClick: onCancel,
|
|
357
|
-
testId: testIds.cancelButton
|
|
358
|
-
}, intl.formatMessage(messages.cancelButton)), jsx(Button, {
|
|
359
|
-
type: "submit",
|
|
360
|
-
appearance: "primary",
|
|
361
|
-
testId: testIds.insertButton
|
|
362
|
-
}, intl.formatMessage(insertButtonMsg)))));
|
|
317
|
+
onMouseLeave: handleMouseLeaveResultItem,
|
|
318
|
+
error: error,
|
|
319
|
+
hasSearchTerm: !!(queryState !== null && queryState !== void 0 && queryState.query.length)
|
|
320
|
+
}), error && ((_errorFallback = errorFallback === null || errorFallback === void 0 ? void 0 : errorFallback(error, retry)) !== null && _errorFallback !== void 0 ? _errorFallback : jsx(LinkSearchError, null)))), jsx(FormFooter, {
|
|
321
|
+
css: !queryState || !(plugins !== null && plugins !== void 0 && plugins.length) ? formFooterMargin : undefined,
|
|
322
|
+
isEditing: isEditing,
|
|
323
|
+
onCancel: onCancel
|
|
324
|
+
}));
|
|
363
325
|
}
|
|
364
326
|
|
|
365
327
|
export default withLinkPickerAnalyticsContext( /*#__PURE__*/memo(LinkPicker));
|
|
@@ -1,10 +1,46 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
/** @jsx jsx */
|
|
4
|
+
import { jsx } from '@emotion/react';
|
|
5
|
+
import { useIntl, FormattedMessage, defineMessages } from 'react-intl-next';
|
|
6
|
+
import Button from '@atlaskit/button';
|
|
7
|
+
export const CONTACT_SUPPORT_LINK = 'https://support.atlassian.com/contact/';
|
|
8
|
+
export const messages = defineMessages({
|
|
9
|
+
searchErrorHeader: {
|
|
10
|
+
id: 'fabric.linkPicker.search.error.heading',
|
|
11
|
+
defaultMessage: 'We’re having trouble loading data.',
|
|
12
|
+
description: 'Heading message shown when a search throws an error'
|
|
13
|
+
},
|
|
14
|
+
searchErrorDescription: {
|
|
15
|
+
id: 'fabric.linkPicker.search.error.description',
|
|
16
|
+
defaultMessage: 'Refresh the page, or contact <a>Atlassian Support</a> if this keeps happening.',
|
|
17
|
+
description: 'Describes possible actions when search throws an error'
|
|
18
|
+
}
|
|
19
|
+
});
|
|
3
20
|
import EmptyState from '@atlaskit/empty-state';
|
|
4
21
|
import GenericErrorSVG from '../../../common/generic-error-svg';
|
|
22
|
+
export const testIds = {
|
|
23
|
+
searchError: 'link-search-error'
|
|
24
|
+
};
|
|
5
25
|
|
|
6
|
-
const LinkSearchError =
|
|
7
|
-
|
|
8
|
-
|
|
26
|
+
const LinkSearchError = () => {
|
|
27
|
+
const intl = useIntl();
|
|
28
|
+
return jsx(EmptyState, {
|
|
29
|
+
testId: testIds.searchError,
|
|
30
|
+
header: intl.formatMessage(messages.searchErrorHeader),
|
|
31
|
+
description: jsx(FormattedMessage, _extends({}, messages.searchErrorDescription, {
|
|
32
|
+
values: {
|
|
33
|
+
a: label => jsx(Button, {
|
|
34
|
+
appearance: "link",
|
|
35
|
+
spacing: "none",
|
|
36
|
+
href: CONTACT_SUPPORT_LINK,
|
|
37
|
+
target: "_blank",
|
|
38
|
+
rel: "noopener noreferrer"
|
|
39
|
+
}, label)
|
|
40
|
+
}
|
|
41
|
+
})),
|
|
42
|
+
renderImage: () => jsx(GenericErrorSVG, null)
|
|
43
|
+
});
|
|
44
|
+
};
|
|
9
45
|
|
|
10
46
|
export default LinkSearchError;
|
|
@@ -1,11 +1,38 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
|
|
1
3
|
/** @jsx jsx */
|
|
4
|
+
import { forwardRef, Fragment } from 'react';
|
|
2
5
|
import { jsx } from '@emotion/react';
|
|
6
|
+
import { defineMessages, FormattedMessage } from 'react-intl-next';
|
|
3
7
|
import Spinner from '@atlaskit/spinner';
|
|
8
|
+
import VisuallyHidden from '@atlaskit/visually-hidden';
|
|
4
9
|
import LinkSearchListItem from '../list-item';
|
|
5
|
-
import { listContainerStyles, spinnerContainerStyles, listStyles } from './styled';
|
|
6
|
-
import { testIds } from '
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
import { listContainerStyles, spinnerContainerStyles, listStyles, listTitleStyles } from './styled';
|
|
11
|
+
import LinkSearchNoResults, { testIds as noResultsTestIds } from './link-search-no-results';
|
|
12
|
+
export const messages = defineMessages({
|
|
13
|
+
titleRecentlyViewed: {
|
|
14
|
+
id: 'fabric.linkPicker.listTitle.recentlyViewed',
|
|
15
|
+
defaultMessage: 'Recently Viewed',
|
|
16
|
+
description: 'Describes type of items shown in the list for screen-reader users'
|
|
17
|
+
},
|
|
18
|
+
titleResults: {
|
|
19
|
+
id: 'fabric.linkPicker.listTitle.results',
|
|
20
|
+
defaultMessage: 'Results',
|
|
21
|
+
description: 'Describes type of items shown in the list for screen-reader users'
|
|
22
|
+
},
|
|
23
|
+
searchLinkResults: {
|
|
24
|
+
id: 'fabric.linkPicker.hyperlink.searchLinkResults',
|
|
25
|
+
defaultMessage: '{count, plural, =0 {no results} one {# result} other {# results}} found',
|
|
26
|
+
description: 'Announce search results for screen-reader users.'
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
export const testIds = { ...noResultsTestIds,
|
|
30
|
+
resultListTitle: 'link-picker-list-title',
|
|
31
|
+
searchResultItem: 'link-search-list-item',
|
|
32
|
+
searchResultList: 'link-search-list',
|
|
33
|
+
searchResultLoadingIndicator: 'link-picker.results-loading-indicator'
|
|
34
|
+
};
|
|
35
|
+
const LinkSearchList = /*#__PURE__*/forwardRef(({
|
|
9
36
|
onSelect,
|
|
10
37
|
onMouseEnter,
|
|
11
38
|
onMouseLeave,
|
|
@@ -16,18 +43,37 @@ const LinkSearchList = ({
|
|
|
16
43
|
ariaControls,
|
|
17
44
|
ariaLabelledBy,
|
|
18
45
|
role,
|
|
19
|
-
id
|
|
20
|
-
|
|
46
|
+
id,
|
|
47
|
+
hasSearchTerm,
|
|
48
|
+
...restProps
|
|
49
|
+
}, ref) => {
|
|
21
50
|
let itemsContent;
|
|
22
51
|
let loadingContent;
|
|
52
|
+
const linkListTitle = hasSearchTerm ? messages.titleResults : messages.titleRecentlyViewed;
|
|
53
|
+
|
|
54
|
+
if ((items === null || items === void 0 ? void 0 : items.length) === 0) {
|
|
55
|
+
return jsx(LinkSearchNoResults, null);
|
|
56
|
+
}
|
|
23
57
|
|
|
24
58
|
if (items && items.length > 0) {
|
|
25
|
-
itemsContent = jsx("
|
|
59
|
+
itemsContent = jsx(Fragment, null, jsx("div", {
|
|
60
|
+
css: listTitleStyles,
|
|
61
|
+
id: testIds.resultListTitle,
|
|
62
|
+
"data-testid": testIds.resultListTitle
|
|
63
|
+
}, jsx(FormattedMessage, linkListTitle)), jsx(VisuallyHidden, {
|
|
64
|
+
id: "fabric.smartcard.linkpicker.suggested.results"
|
|
65
|
+
}, hasSearchTerm && jsx(FormattedMessage, _extends({}, messages.searchLinkResults, {
|
|
66
|
+
values: {
|
|
67
|
+
count: items.length
|
|
68
|
+
},
|
|
69
|
+
"aria-live": "polite",
|
|
70
|
+
"aria-atomic": "true"
|
|
71
|
+
}))), jsx("ul", {
|
|
26
72
|
id: id,
|
|
27
73
|
role: role,
|
|
28
74
|
css: listStyles,
|
|
29
|
-
"aria-controls":
|
|
30
|
-
"aria-labelledby":
|
|
75
|
+
"aria-controls": "fabric.smartcard.linkpicker.suggested.results",
|
|
76
|
+
"aria-labelledby": testIds.resultListTitle,
|
|
31
77
|
"data-testid": testIds.searchResultList
|
|
32
78
|
}, items.map((item, index) => jsx(LinkSearchListItem, {
|
|
33
79
|
id: `${testIds.searchResultItem}-${index}`,
|
|
@@ -39,7 +85,7 @@ const LinkSearchList = ({
|
|
|
39
85
|
onMouseLeave: onMouseLeave,
|
|
40
86
|
onSelect: onSelect,
|
|
41
87
|
key: item.objectId
|
|
42
|
-
})));
|
|
88
|
+
}))));
|
|
43
89
|
}
|
|
44
90
|
|
|
45
91
|
if (isLoading) {
|
|
@@ -51,9 +97,9 @@ const LinkSearchList = ({
|
|
|
51
97
|
}));
|
|
52
98
|
}
|
|
53
99
|
|
|
54
|
-
return jsx("div", {
|
|
100
|
+
return jsx("div", _extends({
|
|
101
|
+
ref: ref,
|
|
55
102
|
css: listContainerStyles
|
|
56
|
-
}, itemsContent, loadingContent);
|
|
57
|
-
};
|
|
58
|
-
|
|
103
|
+
}, restProps), itemsContent, loadingContent);
|
|
104
|
+
});
|
|
59
105
|
export default LinkSearchList;
|