@atlaskit/link-datasource 1.19.21 → 1.19.23
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 +13 -0
- package/dist/cjs/analytics/constants.js +1 -1
- package/dist/cjs/hooks/useValidateAqlText.js +83 -45
- package/dist/cjs/ui/assets-modal/modal/index.js +20 -34
- package/dist/cjs/ui/assets-modal/modal/render-assets-content/index.js +18 -15
- package/dist/cjs/ui/assets-modal/search-container/aql-search-input/index.js +14 -84
- package/dist/cjs/ui/assets-modal/search-container/index.js +1 -1
- package/dist/cjs/ui/jira-issues-modal/basic-filters/hooks/useFilterOptions.js +34 -20
- package/dist/cjs/ui/jira-issues-modal/basic-filters/ui/async-popup-select/index.js +11 -9
- package/dist/cjs/ui/jira-issues-modal/basic-filters/ui/index.js +4 -2
- package/dist/cjs/ui/jira-issues-modal/basic-filters/utils/transformers.js +18 -5
- package/dist/cjs/ui/jira-issues-modal/jira-search-container/index.js +3 -2
- package/dist/cjs/ui/jira-issues-modal/modal/index.js +2 -1
- package/dist/es2019/analytics/constants.js +1 -1
- package/dist/es2019/hooks/useValidateAqlText.js +70 -31
- package/dist/es2019/ui/assets-modal/modal/index.js +5 -4
- package/dist/es2019/ui/assets-modal/modal/render-assets-content/index.js +19 -16
- package/dist/es2019/ui/assets-modal/search-container/aql-search-input/index.js +13 -56
- package/dist/es2019/ui/assets-modal/search-container/index.js +1 -1
- package/dist/es2019/ui/jira-issues-modal/basic-filters/hooks/useFilterOptions.js +13 -3
- package/dist/es2019/ui/jira-issues-modal/basic-filters/ui/async-popup-select/index.js +7 -4
- package/dist/es2019/ui/jira-issues-modal/basic-filters/ui/index.js +5 -2
- package/dist/es2019/ui/jira-issues-modal/basic-filters/utils/transformers.js +19 -5
- package/dist/es2019/ui/jira-issues-modal/jira-search-container/index.js +3 -2
- package/dist/es2019/ui/jira-issues-modal/modal/index.js +2 -1
- package/dist/esm/analytics/constants.js +1 -1
- package/dist/esm/hooks/useValidateAqlText.js +83 -45
- package/dist/esm/ui/assets-modal/modal/index.js +20 -34
- package/dist/esm/ui/assets-modal/modal/render-assets-content/index.js +19 -16
- package/dist/esm/ui/assets-modal/search-container/aql-search-input/index.js +14 -85
- package/dist/esm/ui/assets-modal/search-container/index.js +1 -1
- package/dist/esm/ui/jira-issues-modal/basic-filters/hooks/useFilterOptions.js +34 -20
- package/dist/esm/ui/jira-issues-modal/basic-filters/ui/async-popup-select/index.js +11 -9
- package/dist/esm/ui/jira-issues-modal/basic-filters/ui/index.js +4 -2
- package/dist/esm/ui/jira-issues-modal/basic-filters/utils/transformers.js +18 -5
- package/dist/esm/ui/jira-issues-modal/jira-search-container/index.js +3 -2
- package/dist/esm/ui/jira-issues-modal/modal/index.js +2 -1
- package/dist/types/hooks/useValidateAqlText.d.ts +21 -5
- package/dist/types/services/getAvailableJiraSites.d.ts +1 -5
- package/dist/types/ui/assets-modal/search-container/aql-search-input/index.d.ts +0 -1
- package/dist/types/ui/jira-issues-modal/basic-filters/hooks/useFilterOptions.d.ts +3 -2
- package/dist/types/ui/jira-issues-modal/basic-filters/ui/async-popup-select/index.d.ts +3 -2
- package/dist/types/ui/jira-issues-modal/basic-filters/ui/index.d.ts +3 -2
- package/dist/types/ui/jira-issues-modal/basic-filters/utils/transformers.d.ts +3 -1
- package/dist/types/ui/jira-issues-modal/jira-search-container/index.d.ts +2 -1
- package/dist/types/ui/jira-issues-modal/site-selector/index.d.ts +1 -1
- package/dist/types/ui/jira-issues-modal/types.d.ts +5 -0
- package/dist/types-ts4.5/hooks/useValidateAqlText.d.ts +21 -5
- package/dist/types-ts4.5/services/getAvailableJiraSites.d.ts +1 -5
- package/dist/types-ts4.5/ui/assets-modal/search-container/aql-search-input/index.d.ts +0 -1
- package/dist/types-ts4.5/ui/jira-issues-modal/basic-filters/hooks/useFilterOptions.d.ts +3 -2
- package/dist/types-ts4.5/ui/jira-issues-modal/basic-filters/ui/async-popup-select/index.d.ts +3 -2
- package/dist/types-ts4.5/ui/jira-issues-modal/basic-filters/ui/index.d.ts +3 -2
- package/dist/types-ts4.5/ui/jira-issues-modal/basic-filters/utils/transformers.d.ts +3 -1
- package/dist/types-ts4.5/ui/jira-issues-modal/jira-search-container/index.d.ts +2 -1
- package/dist/types-ts4.5/ui/jira-issues-modal/site-selector/index.d.ts +1 -1
- package/dist/types-ts4.5/ui/jira-issues-modal/types.d.ts +5 -0
- package/package.json +4 -4
|
@@ -36,7 +36,7 @@ var noFilterOptions = function noFilterOptions() {
|
|
|
36
36
|
var SEARCH_DEBOUNCE_MS = exports.SEARCH_DEBOUNCE_MS = 350;
|
|
37
37
|
var AsyncPopupSelect = function AsyncPopupSelect(_ref) {
|
|
38
38
|
var filterType = _ref.filterType,
|
|
39
|
-
|
|
39
|
+
site = _ref.site,
|
|
40
40
|
selection = _ref.selection,
|
|
41
41
|
isJQLHydrating = _ref.isJQLHydrating,
|
|
42
42
|
_ref$onSelectionChang = _ref.onSelectionChange,
|
|
@@ -59,12 +59,14 @@ var AsyncPopupSelect = function AsyncPopupSelect(_ref) {
|
|
|
59
59
|
_useState6 = (0, _slicedToArray2.default)(_useState5, 2),
|
|
60
60
|
sortedOptions = _useState6[0],
|
|
61
61
|
setSortedOptions = _useState6[1];
|
|
62
|
-
var
|
|
62
|
+
var _ref2 = site || {},
|
|
63
|
+
cloudId = _ref2.cloudId;
|
|
64
|
+
var currentSiteCloudId = (0, _react.useRef)(cloudId || '');
|
|
63
65
|
var sortPaginatedResults = (0, _react.useRef)(false); // this is to track pagination for sorting purpose
|
|
64
66
|
|
|
65
67
|
var _useFilterOptions = (0, _useFilterOptions2.useFilterOptions)({
|
|
66
68
|
filterType: filterType,
|
|
67
|
-
|
|
69
|
+
site: site
|
|
68
70
|
}),
|
|
69
71
|
filterOptions = _useFilterOptions.filterOptions,
|
|
70
72
|
fetchFilterOptions = _useFilterOptions.fetchFilterOptions,
|
|
@@ -81,7 +83,7 @@ var AsyncPopupSelect = function AsyncPopupSelect(_ref) {
|
|
|
81
83
|
_useDebouncedCallback2 = (0, _slicedToArray2.default)(_useDebouncedCallback, 1),
|
|
82
84
|
handleDebouncedFetchFilterOptions = _useDebouncedCallback2[0];
|
|
83
85
|
var handleInputChange = (0, _react.useCallback)( /*#__PURE__*/function () {
|
|
84
|
-
var
|
|
86
|
+
var _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(newSearchTerm, actionMeta) {
|
|
85
87
|
return _regenerator.default.wrap(function _callee$(_context) {
|
|
86
88
|
while (1) switch (_context.prev = _context.next) {
|
|
87
89
|
case 0:
|
|
@@ -96,7 +98,7 @@ var AsyncPopupSelect = function AsyncPopupSelect(_ref) {
|
|
|
96
98
|
}, _callee);
|
|
97
99
|
}));
|
|
98
100
|
return function (_x, _x2) {
|
|
99
|
-
return
|
|
101
|
+
return _ref3.apply(this, arguments);
|
|
100
102
|
};
|
|
101
103
|
}(), [handleDebouncedFetchFilterOptions, searchTerm]);
|
|
102
104
|
var handleOptionSelection = (0, _react.useCallback)(function (newValue) {
|
|
@@ -195,7 +197,7 @@ var AsyncPopupSelect = function AsyncPopupSelect(_ref) {
|
|
|
195
197
|
}, [status]); // we only want the sortOptionsOnResolve to run when there is a status change
|
|
196
198
|
|
|
197
199
|
(0, _react.useEffect)(function () {
|
|
198
|
-
if (currentSiteCloudId.current !== cloudId) {
|
|
200
|
+
if (cloudId && currentSiteCloudId.current !== cloudId) {
|
|
199
201
|
currentSiteCloudId.current = cloudId;
|
|
200
202
|
setSortedOptions([]);
|
|
201
203
|
setSearchTerm('');
|
|
@@ -262,9 +264,9 @@ var AsyncPopupSelect = function AsyncPopupSelect(_ref) {
|
|
|
262
264
|
onInputChange: handleInputChange,
|
|
263
265
|
onOpen: handleMenuOpen,
|
|
264
266
|
onClose: handleMenuClose,
|
|
265
|
-
target: function target(
|
|
266
|
-
var isOpen =
|
|
267
|
-
triggerProps = (0, _objectWithoutProperties2.default)(
|
|
267
|
+
target: function target(_ref4) {
|
|
268
|
+
var isOpen = _ref4.isOpen,
|
|
269
|
+
triggerProps = (0, _objectWithoutProperties2.default)(_ref4, _excluded);
|
|
268
270
|
return /*#__PURE__*/_react.default.createElement(_trigger.default, (0, _extends2.default)({}, triggerProps, {
|
|
269
271
|
filterType: filterType,
|
|
270
272
|
selectedOptions: selectedOptions,
|
|
@@ -18,13 +18,15 @@ var basicFilterContainerStyles = (0, _primitives.xcss)({
|
|
|
18
18
|
});
|
|
19
19
|
var BasicFilterContainer = function BasicFilterContainer(_ref) {
|
|
20
20
|
var jql = _ref.jql,
|
|
21
|
-
|
|
21
|
+
site = _ref.site,
|
|
22
22
|
onChange = _ref.onChange,
|
|
23
23
|
selections = _ref.selections,
|
|
24
24
|
isJQLHydrating = _ref.isJQLHydrating;
|
|
25
25
|
var extractedFilterValues = (0, _react.useMemo)(function () {
|
|
26
26
|
return isJQLHydrating ? (0, _extractValuesFromNonComplexJQL.extractValuesFromNonComplexJQL)(jql) : {};
|
|
27
27
|
}, [isJQLHydrating, jql]);
|
|
28
|
+
var _ref2 = site || {},
|
|
29
|
+
cloudId = _ref2.cloudId;
|
|
28
30
|
return /*#__PURE__*/_react.default.createElement(_primitives.Flex, {
|
|
29
31
|
xcss: basicFilterContainerStyles,
|
|
30
32
|
gap: "space.100",
|
|
@@ -33,7 +35,7 @@ var BasicFilterContainer = function BasicFilterContainer(_ref) {
|
|
|
33
35
|
var _extractedFilterValue;
|
|
34
36
|
var shouldShowHydrationLoader = isJQLHydrating && ((_extractedFilterValue = extractedFilterValues[filter]) === null || _extractedFilterValue === void 0 ? void 0 : _extractedFilterValue.length) > 0;
|
|
35
37
|
return /*#__PURE__*/_react.default.createElement(_asyncPopupSelect.default, {
|
|
36
|
-
|
|
38
|
+
site: site,
|
|
37
39
|
filterType: filter,
|
|
38
40
|
key: filter,
|
|
39
41
|
selection: selections[filter] || [],
|
|
@@ -24,6 +24,15 @@ var getLozengeAppearance = function getLozengeAppearance(colorName) {
|
|
|
24
24
|
return _types.appearanceMap[colorName];
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
|
+
var checkAndConvertToAbsoluteUrl = function checkAndConvertToAbsoluteUrl(url, siteUrl) {
|
|
28
|
+
if (!url) {
|
|
29
|
+
return '';
|
|
30
|
+
}
|
|
31
|
+
if (/^data:(.*)/.test(url) || /^http(.*)/.test(url) || !siteUrl) {
|
|
32
|
+
return url;
|
|
33
|
+
}
|
|
34
|
+
return "".concat(siteUrl).concat(url);
|
|
35
|
+
};
|
|
27
36
|
function mapNodeToOption(_ref) {
|
|
28
37
|
var displayName = _ref.displayName,
|
|
29
38
|
jqlTerm = _ref.jqlTerm,
|
|
@@ -31,7 +40,8 @@ function mapNodeToOption(_ref) {
|
|
|
31
40
|
issueTypes = _ref.issueTypes,
|
|
32
41
|
project = _ref.project,
|
|
33
42
|
statusCategory = _ref.statusCategory,
|
|
34
|
-
user = _ref.user
|
|
43
|
+
user = _ref.user,
|
|
44
|
+
siteUrl = _ref.siteUrl;
|
|
35
45
|
try {
|
|
36
46
|
var baseProps = {
|
|
37
47
|
label: displayName,
|
|
@@ -56,14 +66,14 @@ function mapNodeToOption(_ref) {
|
|
|
56
66
|
var _project$avatar;
|
|
57
67
|
return _objectSpread(_objectSpread({}, baseProps), {}, {
|
|
58
68
|
optionType: 'iconLabel',
|
|
59
|
-
icon: (_project$avatar = project.avatar) === null || _project$avatar === void 0 ? void 0 : _project$avatar.small
|
|
69
|
+
icon: checkAndConvertToAbsoluteUrl((_project$avatar = project.avatar) === null || _project$avatar === void 0 ? void 0 : _project$avatar.small, siteUrl)
|
|
60
70
|
});
|
|
61
71
|
}
|
|
62
72
|
if (issueTypes) {
|
|
63
73
|
var _issueTypes$;
|
|
64
74
|
return _objectSpread(_objectSpread({}, baseProps), {}, {
|
|
65
75
|
optionType: 'iconLabel',
|
|
66
|
-
icon: (_issueTypes$ = issueTypes[0]) === null || _issueTypes$ === void 0 ? void 0 : _issueTypes$.avatar.small
|
|
76
|
+
icon: checkAndConvertToAbsoluteUrl((_issueTypes$ = issueTypes[0]) === null || _issueTypes$ === void 0 ? void 0 : _issueTypes$.avatar.small, siteUrl)
|
|
67
77
|
});
|
|
68
78
|
}
|
|
69
79
|
if (statusCategory) {
|
|
@@ -102,9 +112,12 @@ function mapHydrateResponseData(_ref2) {
|
|
|
102
112
|
}
|
|
103
113
|
function mapFieldValuesToFilterOptions(_ref5) {
|
|
104
114
|
var _data$jira2;
|
|
105
|
-
var data = _ref5.data
|
|
115
|
+
var data = _ref5.data,
|
|
116
|
+
siteUrl = _ref5.siteUrl;
|
|
106
117
|
return (data === null || data === void 0 || (_data$jira2 = data.jira) === null || _data$jira2 === void 0 || (_data$jira2 = _data$jira2.jqlBuilder) === null || _data$jira2 === void 0 || (_data$jira2 = _data$jira2.fieldValues) === null || _data$jira2 === void 0 || (_data$jira2 = _data$jira2.edges) === null || _data$jira2 === void 0 ? void 0 : _data$jira2.map(function (edge) {
|
|
107
|
-
return edge.node ? mapNodeToOption(edge.node)
|
|
118
|
+
return edge.node ? mapNodeToOption(_objectSpread(_objectSpread({}, edge.node), {}, {
|
|
119
|
+
siteUrl: siteUrl
|
|
120
|
+
})) : null;
|
|
108
121
|
}).filter(isNonNullSelectOption)) || [];
|
|
109
122
|
}
|
|
110
123
|
function mapFieldValuesToTotalCount(_ref6) {
|
|
@@ -48,7 +48,8 @@ var JiraSearchContainer = exports.JiraSearchContainer = function JiraSearchConta
|
|
|
48
48
|
initialSearchMethod = props.initialSearchMethod,
|
|
49
49
|
setSearchBarJql = props.setSearchBarJql,
|
|
50
50
|
_props$searchBarJql = props.searchBarJql,
|
|
51
|
-
searchBarJql = _props$searchBarJql === void 0 ? DEFAULT_JQL_QUERY : _props$searchBarJql
|
|
51
|
+
searchBarJql = _props$searchBarJql === void 0 ? DEFAULT_JQL_QUERY : _props$searchBarJql,
|
|
52
|
+
site = props.site;
|
|
52
53
|
var _ref = parameters || {},
|
|
53
54
|
currentCloudId = _ref.cloudId;
|
|
54
55
|
var _useIntl = (0, _reactIntlNext.useIntl)(),
|
|
@@ -228,7 +229,7 @@ var JiraSearchContainer = exports.JiraSearchContainer = function JiraSearchConta
|
|
|
228
229
|
searchTerm: basicSearchTerm
|
|
229
230
|
}), showBasicFilters && (0, _react2.jsx)(_basicFilters.BasicFilters, {
|
|
230
231
|
jql: searchBarJql,
|
|
231
|
-
|
|
232
|
+
site: site,
|
|
232
233
|
onChange: handleBasicFilterSelectionChange,
|
|
233
234
|
selections: filterSelections,
|
|
234
235
|
isJQLHydrating: basicFilterHydrationStatus === 'loading'
|
|
@@ -570,7 +570,8 @@ var PlainJiraIssuesConfigModal = exports.PlainJiraIssuesConfigModal = function P
|
|
|
570
570
|
parameters: parameters,
|
|
571
571
|
onSearch: onSearch,
|
|
572
572
|
initialSearchMethod: initialSearchMethod,
|
|
573
|
-
onSearchMethodChange: setCurrentSearchMethod
|
|
573
|
+
onSearchMethodChange: setCurrentSearchMethod,
|
|
574
|
+
site: selectedJiraSite
|
|
574
575
|
}), currentViewMode === 'count' ? renderCountModeContent() : renderIssuesModeContent()) : (0, _react2.jsx)(_noInstances.NoInstancesView, null)), (0, _react2.jsx)(_modalDialog.ModalFooter, null, shouldShowIssueCount && (0, _react2.jsx)("div", {
|
|
575
576
|
"data-testid": "jira-jql-datasource-modal-total-issues-count",
|
|
576
577
|
css: issueCountStyles
|
|
@@ -1,44 +1,83 @@
|
|
|
1
|
-
import { useCallback, useState } from 'react';
|
|
1
|
+
import { useCallback, useRef, useState } from 'react';
|
|
2
2
|
import { useDatasourceAnalyticsEvents } from '../analytics';
|
|
3
3
|
import { validateAql } from '../services/cmdbService';
|
|
4
|
-
export const
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
4
|
+
export const SEARCH_DEBOUNCE = 350;
|
|
5
|
+
export const useValidateAqlText = (workspaceId, initialValue) => {
|
|
6
|
+
const timeout = useRef();
|
|
7
|
+
const lastValue = useRef('');
|
|
8
|
+
const lastResult = useRef(Promise.resolve(undefined));
|
|
9
|
+
const [lastValidationResult, setLastValidationResult] = useState(initialValue.trim() === '' ? {
|
|
10
|
+
type: 'idle'
|
|
11
|
+
} : {
|
|
12
|
+
type: 'loading'
|
|
13
|
+
});
|
|
8
14
|
const {
|
|
9
15
|
fireEvent
|
|
10
16
|
} = useDatasourceAnalyticsEvents();
|
|
17
|
+
|
|
18
|
+
// We return undefined when valid and 'error' when invalid
|
|
11
19
|
const validateAqlText = useCallback(async aql => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
if (aql !== null && aql !== void 0 && aql.trim()) {
|
|
21
|
+
try {
|
|
22
|
+
const validateAqlResponse = await validateAql(workspaceId, {
|
|
23
|
+
qlQuery: aql
|
|
24
|
+
}, fireEvent);
|
|
25
|
+
if (validateAqlResponse.isValid) {
|
|
26
|
+
setLastValidationResult({
|
|
27
|
+
type: 'valid',
|
|
28
|
+
validatedAql: aql
|
|
29
|
+
});
|
|
30
|
+
return undefined;
|
|
31
|
+
} else {
|
|
32
|
+
var _validateAqlResponse$, _validateAqlResponse$2;
|
|
33
|
+
setLastValidationResult({
|
|
34
|
+
type: 'invalid',
|
|
35
|
+
error: (_validateAqlResponse$ = (_validateAqlResponse$2 = validateAqlResponse.errors) === null || _validateAqlResponse$2 === void 0 ? void 0 : _validateAqlResponse$2.iql) !== null && _validateAqlResponse$ !== void 0 ? _validateAqlResponse$ : ''
|
|
36
|
+
});
|
|
37
|
+
return 'error';
|
|
38
|
+
}
|
|
39
|
+
} catch (err) {
|
|
40
|
+
setLastValidationResult({
|
|
41
|
+
type: 'invalid',
|
|
42
|
+
error: ''
|
|
43
|
+
});
|
|
44
|
+
return 'error';
|
|
29
45
|
}
|
|
30
|
-
} finally {
|
|
31
|
-
setLoading(false);
|
|
32
46
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
47
|
+
setLastValidationResult({
|
|
48
|
+
type: 'idle'
|
|
49
|
+
});
|
|
50
|
+
return undefined;
|
|
37
51
|
}, [workspaceId, fireEvent]);
|
|
52
|
+
|
|
53
|
+
/* Debounce async validation for input, validation is also called on every field change
|
|
54
|
+
in a form so we need to also memoize. The async validate function is expected to either:
|
|
55
|
+
Immediately return a promise (which is then collected into an array, every single time validation is run),
|
|
56
|
+
or immediately return either undefined or an error message */
|
|
57
|
+
const debouncedValidation = value => new Promise(resolve => {
|
|
58
|
+
if (timeout.current) {
|
|
59
|
+
timeout.current();
|
|
60
|
+
}
|
|
61
|
+
if (value !== lastValue.current) {
|
|
62
|
+
const timerId = setTimeout(() => {
|
|
63
|
+
setLastValidationResult({
|
|
64
|
+
type: 'loading'
|
|
65
|
+
});
|
|
66
|
+
lastValue.current = value;
|
|
67
|
+
lastResult.current = validateAqlText(value);
|
|
68
|
+
resolve(lastResult.current);
|
|
69
|
+
}, SEARCH_DEBOUNCE);
|
|
70
|
+
timeout.current = () => {
|
|
71
|
+
clearTimeout(timerId);
|
|
72
|
+
resolve('debouncing');
|
|
73
|
+
};
|
|
74
|
+
} else {
|
|
75
|
+
resolve(lastResult.current);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
38
78
|
return {
|
|
39
|
-
|
|
79
|
+
debouncedValidation,
|
|
40
80
|
validateAqlText,
|
|
41
|
-
|
|
42
|
-
validateAqlTextError: error
|
|
81
|
+
lastValidationResult
|
|
43
82
|
};
|
|
44
83
|
};
|
|
@@ -259,7 +259,7 @@ const PlainAssetsConfigModal = props => {
|
|
|
259
259
|
}).fire(EVENT_CHANNEL);
|
|
260
260
|
onCancel();
|
|
261
261
|
}, [analyticsPayload, onCancel]);
|
|
262
|
-
const handleOnSearch = useCallback(
|
|
262
|
+
const handleOnSearch = useCallback((searchAql, searchSchemaId) => {
|
|
263
263
|
if (schemaId !== searchSchemaId || aql !== searchAql || status === 'rejected') {
|
|
264
264
|
searchCount.current++;
|
|
265
265
|
if (schemaId !== searchSchemaId) {
|
|
@@ -269,7 +269,8 @@ const PlainAssetsConfigModal = props => {
|
|
|
269
269
|
userInteractionActions.current.add(DatasourceAction.QUERY_UPDATED);
|
|
270
270
|
}
|
|
271
271
|
reset({
|
|
272
|
-
shouldResetColumns: true
|
|
272
|
+
shouldResetColumns: true,
|
|
273
|
+
shouldForceRequest: true
|
|
273
274
|
});
|
|
274
275
|
setAql(searchAql);
|
|
275
276
|
setSchemaId(searchSchemaId);
|
|
@@ -300,7 +301,7 @@ const PlainAssetsConfigModal = props => {
|
|
|
300
301
|
return jsx(AssetsSearchContainer, {
|
|
301
302
|
workspaceId: workspaceId,
|
|
302
303
|
initialSearchData: {
|
|
303
|
-
aql,
|
|
304
|
+
aql: initialParameters === null || initialParameters === void 0 ? void 0 : initialParameters.aql,
|
|
304
305
|
objectSchema: existingObjectSchema,
|
|
305
306
|
objectSchemas
|
|
306
307
|
},
|
|
@@ -309,7 +310,7 @@ const PlainAssetsConfigModal = props => {
|
|
|
309
310
|
isSearching: status === 'loading'
|
|
310
311
|
});
|
|
311
312
|
}
|
|
312
|
-
}, [errorState, workspaceId, assetsClientLoading, aql, existingObjectSchema, objectSchemas, handleOnSearch, status]);
|
|
313
|
+
}, [errorState, workspaceId, assetsClientLoading, initialParameters === null || initialParameters === void 0 ? void 0 : initialParameters.aql, existingObjectSchema, objectSchemas, handleOnSearch, status]);
|
|
313
314
|
return jsx(IntlMessagesProvider, {
|
|
314
315
|
defaultMessages: i18nEN,
|
|
315
316
|
loaderFn: fetchMessagesForLocale
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @jsx jsx */
|
|
2
|
-
import { useMemo } from 'react';
|
|
2
|
+
import { useCallback, useMemo } from 'react';
|
|
3
3
|
import { css, jsx } from '@emotion/react';
|
|
4
4
|
import { N40 } from '@atlaskit/theme/colors';
|
|
5
5
|
import { AccessRequired } from '../../../common/error-state/access-required';
|
|
@@ -74,19 +74,22 @@ export const RenderAssetsContent = props => {
|
|
|
74
74
|
onVisibleColumnKeysChange: onVisibleColumnKeysChange,
|
|
75
75
|
parentContainerRenderInstanceId: modalRenderInstanceId
|
|
76
76
|
})), [columns, defaultVisibleColumnKeys, hasNextPage, loadDatasourceDetails, onNextPage, onVisibleColumnKeysChange, responseItems, status, visibleColumnKeys, modalRenderInstanceId]);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
77
|
+
const renderAssetsContentView = useCallback(() => {
|
|
78
|
+
if (isFetchingInitialData) {
|
|
79
|
+
// Placing this check first as it's a priority before all others
|
|
80
|
+
return jsx(LoadingView, null);
|
|
81
|
+
} else if (status === 'rejected') {
|
|
82
|
+
return jsx(RejectedView, null);
|
|
83
|
+
} else if (status === 'unauthorized') {
|
|
84
|
+
return jsx(UnauthorizedView, null);
|
|
85
|
+
} else if (status === 'empty') {
|
|
86
|
+
return jsx(EmptyView, null);
|
|
87
|
+
} else if (resolvedWithNoResults) {
|
|
88
|
+
return jsx(NoResultsView, null);
|
|
89
|
+
} else if (status === 'loading' && !columns.length) {
|
|
90
|
+
return jsx(LoadingView, null);
|
|
91
|
+
}
|
|
92
|
+
return issueLikeDataTableView;
|
|
93
|
+
}, [columns.length, isFetchingInitialData, issueLikeDataTableView, resolvedWithNoResults, status]);
|
|
94
|
+
return renderAssetsContentView();
|
|
92
95
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
2
|
/** @jsx jsx */
|
|
3
|
-
import { Fragment
|
|
3
|
+
import { Fragment } from 'react';
|
|
4
4
|
import { css, jsx } from '@emotion/react';
|
|
5
5
|
import { useIntl } from 'react-intl-next';
|
|
6
6
|
import { LoadingButton } from '@atlaskit/button';
|
|
@@ -17,10 +17,6 @@ import { useValidateAqlText } from '../../../../hooks/useValidateAqlText';
|
|
|
17
17
|
import { aqlKey } from '../../../../types/assets/types';
|
|
18
18
|
import { FieldContainer } from '../styled';
|
|
19
19
|
import { searchInputMessages } from './messages';
|
|
20
|
-
|
|
21
|
-
/* Meta isn't exported in @atlaskit/form
|
|
22
|
-
Taken from packages/design-system/form/src/field.tsx */
|
|
23
|
-
|
|
24
20
|
const buttonBaseStyles = css({
|
|
25
21
|
display: 'flex',
|
|
26
22
|
height: '100%',
|
|
@@ -34,15 +30,14 @@ const AQLSupportDocumentLink = 'https://support.atlassian.com/jira-service-manag
|
|
|
34
30
|
const searchButtonStyles = css({
|
|
35
31
|
marginRight: "var(--ds-space-075, 6px)"
|
|
36
32
|
});
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (value && meta !== null && meta !== void 0 && meta.validating) {
|
|
33
|
+
const renderValidatorIcon = lastValidationResult => {
|
|
34
|
+
if (lastValidationResult.type === 'loading') {
|
|
40
35
|
return jsx(Spinner, {
|
|
41
36
|
size: "medium",
|
|
42
37
|
testId: "assets-datasource-modal--aql-validating"
|
|
43
38
|
});
|
|
44
39
|
}
|
|
45
|
-
if (
|
|
40
|
+
if (lastValidationResult.type === 'invalid') {
|
|
46
41
|
return jsx(CrossCircleIcon, {
|
|
47
42
|
label: "label",
|
|
48
43
|
primaryColor: "red",
|
|
@@ -50,7 +45,7 @@ const renderValidatorIcon = (value, error, meta) => {
|
|
|
50
45
|
testId: "assets-datasource-modal--aql-invalid"
|
|
51
46
|
});
|
|
52
47
|
}
|
|
53
|
-
if (
|
|
48
|
+
if (lastValidationResult.type === 'valid') {
|
|
54
49
|
return jsx(CheckCircleIcon, {
|
|
55
50
|
label: "label",
|
|
56
51
|
primaryColor: "green",
|
|
@@ -73,61 +68,23 @@ export const AqlSearchInput = ({
|
|
|
73
68
|
const {
|
|
74
69
|
formatMessage
|
|
75
70
|
} = useIntl();
|
|
76
|
-
const timeout = useRef();
|
|
77
|
-
const lastValue = useRef(value);
|
|
78
|
-
const lastResult = useRef(Promise.resolve(undefined));
|
|
79
|
-
const [message, setMessage] = useState(null);
|
|
80
71
|
const {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// Validation expects undefined when valid and a string as an error message when invalid
|
|
85
|
-
const handleValidation = useCallback(async newUnvalidatedQlQuery => {
|
|
86
|
-
if (!newUnvalidatedQlQuery) {
|
|
87
|
-
return undefined;
|
|
88
|
-
}
|
|
89
|
-
const validation = await validateAqlText(newUnvalidatedQlQuery);
|
|
90
|
-
setMessage(validation.message);
|
|
91
|
-
return validation.isValid ? undefined : 'invalid';
|
|
92
|
-
}, [validateAqlText]);
|
|
93
|
-
|
|
94
|
-
/* Debounce async validation for input, validation is also called on every field change
|
|
95
|
-
in a form so we need to also memoize. The async validate function is expected to either:
|
|
96
|
-
Immediately return a promise (which is then collected into an array, every single time validation is run),
|
|
97
|
-
or immediately return either undefined or an error message */
|
|
98
|
-
const debouncedMemoizedValidation = value => new Promise(resolve => {
|
|
99
|
-
if (timeout.current) {
|
|
100
|
-
timeout.current();
|
|
101
|
-
}
|
|
102
|
-
if (value !== lastValue.current) {
|
|
103
|
-
const timerId = setTimeout(() => {
|
|
104
|
-
lastValue.current = value;
|
|
105
|
-
lastResult.current = handleValidation(value);
|
|
106
|
-
resolve(lastResult.current);
|
|
107
|
-
}, SEARCH_DEBOUNCE_MS);
|
|
108
|
-
timeout.current = () => {
|
|
109
|
-
clearTimeout(timerId);
|
|
110
|
-
resolve('debouncing');
|
|
111
|
-
};
|
|
112
|
-
} else {
|
|
113
|
-
resolve(lastResult.current);
|
|
114
|
-
}
|
|
115
|
-
});
|
|
72
|
+
debouncedValidation,
|
|
73
|
+
lastValidationResult
|
|
74
|
+
} = useValidateAqlText(workspaceId, value);
|
|
116
75
|
return jsx(FieldContainer, null, jsx(Field, {
|
|
117
76
|
name: aqlKey,
|
|
118
77
|
defaultValue: value,
|
|
119
|
-
validate:
|
|
78
|
+
validate: debouncedValidation
|
|
120
79
|
}, ({
|
|
121
|
-
fieldProps
|
|
122
|
-
meta,
|
|
123
|
-
error
|
|
80
|
+
fieldProps
|
|
124
81
|
}) => jsx(Fragment, null, jsx(Textfield, _extends({}, fieldProps, {
|
|
125
82
|
elemBeforeInput: jsx("span", {
|
|
126
83
|
style: {
|
|
127
84
|
paddingLeft: 6,
|
|
128
85
|
width: 24
|
|
129
86
|
}
|
|
130
|
-
}, renderValidatorIcon(
|
|
87
|
+
}, renderValidatorIcon(lastValidationResult)),
|
|
131
88
|
elemAfterInput: jsx(Fragment, null, jsx(Tooltip, {
|
|
132
89
|
content: formatMessage(searchInputMessages.helpTooltipText),
|
|
133
90
|
position: "bottom"
|
|
@@ -151,9 +108,9 @@ export const AqlSearchInput = ({
|
|
|
151
108
|
spacing: "none",
|
|
152
109
|
testId: "assets-datasource-modal--aql-search-button",
|
|
153
110
|
type: "submit",
|
|
154
|
-
isDisabled:
|
|
111
|
+
isDisabled: lastValidationResult.type !== 'valid'
|
|
155
112
|
})),
|
|
156
113
|
placeholder: formatMessage(searchInputMessages.placeholder),
|
|
157
114
|
testId: testId
|
|
158
|
-
})),
|
|
115
|
+
})), lastValidationResult.type === 'invalid' && lastValidationResult.error && jsx(ErrorMessage, null, lastValidationResult.error))));
|
|
159
116
|
};
|
|
@@ -24,7 +24,7 @@ export const AssetsSearchContainer = props => {
|
|
|
24
24
|
aql,
|
|
25
25
|
objectSchema
|
|
26
26
|
} = searchFormValues;
|
|
27
|
-
if (objectSchema) {
|
|
27
|
+
if (aql && objectSchema) {
|
|
28
28
|
fireEvent('ui.aqlEditor.searched', {});
|
|
29
29
|
// Pass the validated aql and object schema back to modal
|
|
30
30
|
onSearch(aql, objectSchema.value);
|
|
@@ -10,7 +10,7 @@ export const getAssigneeUnassignedFilterOption = formatMessage => ({
|
|
|
10
10
|
});
|
|
11
11
|
export const useFilterOptions = ({
|
|
12
12
|
filterType,
|
|
13
|
-
|
|
13
|
+
site
|
|
14
14
|
}) => {
|
|
15
15
|
const {
|
|
16
16
|
formatMessage
|
|
@@ -21,6 +21,10 @@ export const useFilterOptions = ({
|
|
|
21
21
|
const [errors, setErrors] = useState([]);
|
|
22
22
|
const [nextPageCursor, setNextPageCursor] = useState(undefined);
|
|
23
23
|
const initialData = useRef();
|
|
24
|
+
const {
|
|
25
|
+
cloudId,
|
|
26
|
+
url: siteUrl
|
|
27
|
+
} = site || {};
|
|
24
28
|
const {
|
|
25
29
|
getFieldValues
|
|
26
30
|
} = useBasicFilterAGG();
|
|
@@ -28,6 +32,9 @@ export const useFilterOptions = ({
|
|
|
28
32
|
pageCursor,
|
|
29
33
|
searchString
|
|
30
34
|
} = {}) => {
|
|
35
|
+
if (!cloudId) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
31
38
|
const isNewSearch = !pageCursor;
|
|
32
39
|
isNewSearch ? setStatus('loading') : setStatus('loadingMore');
|
|
33
40
|
const isRequestLikeInitialSearch = !pageCursor && !searchString;
|
|
@@ -47,7 +54,10 @@ export const useFilterOptions = ({
|
|
|
47
54
|
setErrors(response.errors);
|
|
48
55
|
return;
|
|
49
56
|
}
|
|
50
|
-
const mappedResponse = mapFieldValuesToFilterOptions(
|
|
57
|
+
const mappedResponse = mapFieldValuesToFilterOptions({
|
|
58
|
+
...response,
|
|
59
|
+
siteUrl
|
|
60
|
+
});
|
|
51
61
|
let mappedTotalCount = mapFieldValuesToTotalCount(response);
|
|
52
62
|
if (isNewSearch) {
|
|
53
63
|
if (isRequestLikeInitialSearch) {
|
|
@@ -77,7 +87,7 @@ export const useFilterOptions = ({
|
|
|
77
87
|
setStatus('rejected');
|
|
78
88
|
setErrors([error]);
|
|
79
89
|
}
|
|
80
|
-
}, [cloudId, filterOptions, filterType, formatMessage, getFieldValues]);
|
|
90
|
+
}, [cloudId, filterOptions, filterType, formatMessage, getFieldValues, siteUrl]);
|
|
81
91
|
useEffect(() => {
|
|
82
92
|
if (status !== 'rejected' && errors.length !== 0) {
|
|
83
93
|
setErrors([]);
|
|
@@ -18,7 +18,7 @@ const noFilterOptions = () => true;
|
|
|
18
18
|
export const SEARCH_DEBOUNCE_MS = 350;
|
|
19
19
|
const AsyncPopupSelect = ({
|
|
20
20
|
filterType,
|
|
21
|
-
|
|
21
|
+
site,
|
|
22
22
|
selection,
|
|
23
23
|
isJQLHydrating,
|
|
24
24
|
onSelectionChange = () => {},
|
|
@@ -33,7 +33,10 @@ const AsyncPopupSelect = ({
|
|
|
33
33
|
const [searchTerm, setSearchTerm] = useState('');
|
|
34
34
|
const [selectedOptions, setSelectedOptions] = useState(selection);
|
|
35
35
|
const [sortedOptions, setSortedOptions] = useState([]);
|
|
36
|
-
const
|
|
36
|
+
const {
|
|
37
|
+
cloudId
|
|
38
|
+
} = site || {};
|
|
39
|
+
const currentSiteCloudId = useRef(cloudId || '');
|
|
37
40
|
const sortPaginatedResults = useRef(false); // this is to track pagination for sorting purpose
|
|
38
41
|
|
|
39
42
|
const {
|
|
@@ -46,7 +49,7 @@ const AsyncPopupSelect = ({
|
|
|
46
49
|
errors
|
|
47
50
|
} = useFilterOptions({
|
|
48
51
|
filterType,
|
|
49
|
-
|
|
52
|
+
site
|
|
50
53
|
});
|
|
51
54
|
const [handleDebouncedFetchFilterOptions] = useDebouncedCallback(searchString => {
|
|
52
55
|
fetchFilterOptions({
|
|
@@ -147,7 +150,7 @@ const AsyncPopupSelect = ({
|
|
|
147
150
|
}, [status]); // we only want the sortOptionsOnResolve to run when there is a status change
|
|
148
151
|
|
|
149
152
|
useEffect(() => {
|
|
150
|
-
if (currentSiteCloudId.current !== cloudId) {
|
|
153
|
+
if (cloudId && currentSiteCloudId.current !== cloudId) {
|
|
151
154
|
currentSiteCloudId.current = cloudId;
|
|
152
155
|
setSortedOptions([]);
|
|
153
156
|
setSearchTerm('');
|
|
@@ -8,12 +8,15 @@ const basicFilterContainerStyles = xcss({
|
|
|
8
8
|
});
|
|
9
9
|
const BasicFilterContainer = ({
|
|
10
10
|
jql,
|
|
11
|
-
|
|
11
|
+
site,
|
|
12
12
|
onChange,
|
|
13
13
|
selections,
|
|
14
14
|
isJQLHydrating
|
|
15
15
|
}) => {
|
|
16
16
|
const extractedFilterValues = useMemo(() => isJQLHydrating ? extractValuesFromNonComplexJQL(jql) : {}, [isJQLHydrating, jql]);
|
|
17
|
+
const {
|
|
18
|
+
cloudId
|
|
19
|
+
} = site || {};
|
|
17
20
|
return /*#__PURE__*/React.createElement(Flex, {
|
|
18
21
|
xcss: basicFilterContainerStyles,
|
|
19
22
|
gap: "space.100",
|
|
@@ -22,7 +25,7 @@ const BasicFilterContainer = ({
|
|
|
22
25
|
var _extractedFilterValue;
|
|
23
26
|
const shouldShowHydrationLoader = isJQLHydrating && ((_extractedFilterValue = extractedFilterValues[filter]) === null || _extractedFilterValue === void 0 ? void 0 : _extractedFilterValue.length) > 0;
|
|
24
27
|
return /*#__PURE__*/React.createElement(AsyncPopupSelect, {
|
|
25
|
-
|
|
28
|
+
site: site,
|
|
26
29
|
filterType: filter,
|
|
27
30
|
key: filter,
|
|
28
31
|
selection: selections[filter] || [],
|