@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.
Files changed (58) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/analytics/constants.js +1 -1
  3. package/dist/cjs/hooks/useValidateAqlText.js +83 -45
  4. package/dist/cjs/ui/assets-modal/modal/index.js +20 -34
  5. package/dist/cjs/ui/assets-modal/modal/render-assets-content/index.js +18 -15
  6. package/dist/cjs/ui/assets-modal/search-container/aql-search-input/index.js +14 -84
  7. package/dist/cjs/ui/assets-modal/search-container/index.js +1 -1
  8. package/dist/cjs/ui/jira-issues-modal/basic-filters/hooks/useFilterOptions.js +34 -20
  9. package/dist/cjs/ui/jira-issues-modal/basic-filters/ui/async-popup-select/index.js +11 -9
  10. package/dist/cjs/ui/jira-issues-modal/basic-filters/ui/index.js +4 -2
  11. package/dist/cjs/ui/jira-issues-modal/basic-filters/utils/transformers.js +18 -5
  12. package/dist/cjs/ui/jira-issues-modal/jira-search-container/index.js +3 -2
  13. package/dist/cjs/ui/jira-issues-modal/modal/index.js +2 -1
  14. package/dist/es2019/analytics/constants.js +1 -1
  15. package/dist/es2019/hooks/useValidateAqlText.js +70 -31
  16. package/dist/es2019/ui/assets-modal/modal/index.js +5 -4
  17. package/dist/es2019/ui/assets-modal/modal/render-assets-content/index.js +19 -16
  18. package/dist/es2019/ui/assets-modal/search-container/aql-search-input/index.js +13 -56
  19. package/dist/es2019/ui/assets-modal/search-container/index.js +1 -1
  20. package/dist/es2019/ui/jira-issues-modal/basic-filters/hooks/useFilterOptions.js +13 -3
  21. package/dist/es2019/ui/jira-issues-modal/basic-filters/ui/async-popup-select/index.js +7 -4
  22. package/dist/es2019/ui/jira-issues-modal/basic-filters/ui/index.js +5 -2
  23. package/dist/es2019/ui/jira-issues-modal/basic-filters/utils/transformers.js +19 -5
  24. package/dist/es2019/ui/jira-issues-modal/jira-search-container/index.js +3 -2
  25. package/dist/es2019/ui/jira-issues-modal/modal/index.js +2 -1
  26. package/dist/esm/analytics/constants.js +1 -1
  27. package/dist/esm/hooks/useValidateAqlText.js +83 -45
  28. package/dist/esm/ui/assets-modal/modal/index.js +20 -34
  29. package/dist/esm/ui/assets-modal/modal/render-assets-content/index.js +19 -16
  30. package/dist/esm/ui/assets-modal/search-container/aql-search-input/index.js +14 -85
  31. package/dist/esm/ui/assets-modal/search-container/index.js +1 -1
  32. package/dist/esm/ui/jira-issues-modal/basic-filters/hooks/useFilterOptions.js +34 -20
  33. package/dist/esm/ui/jira-issues-modal/basic-filters/ui/async-popup-select/index.js +11 -9
  34. package/dist/esm/ui/jira-issues-modal/basic-filters/ui/index.js +4 -2
  35. package/dist/esm/ui/jira-issues-modal/basic-filters/utils/transformers.js +18 -5
  36. package/dist/esm/ui/jira-issues-modal/jira-search-container/index.js +3 -2
  37. package/dist/esm/ui/jira-issues-modal/modal/index.js +2 -1
  38. package/dist/types/hooks/useValidateAqlText.d.ts +21 -5
  39. package/dist/types/services/getAvailableJiraSites.d.ts +1 -5
  40. package/dist/types/ui/assets-modal/search-container/aql-search-input/index.d.ts +0 -1
  41. package/dist/types/ui/jira-issues-modal/basic-filters/hooks/useFilterOptions.d.ts +3 -2
  42. package/dist/types/ui/jira-issues-modal/basic-filters/ui/async-popup-select/index.d.ts +3 -2
  43. package/dist/types/ui/jira-issues-modal/basic-filters/ui/index.d.ts +3 -2
  44. package/dist/types/ui/jira-issues-modal/basic-filters/utils/transformers.d.ts +3 -1
  45. package/dist/types/ui/jira-issues-modal/jira-search-container/index.d.ts +2 -1
  46. package/dist/types/ui/jira-issues-modal/site-selector/index.d.ts +1 -1
  47. package/dist/types/ui/jira-issues-modal/types.d.ts +5 -0
  48. package/dist/types-ts4.5/hooks/useValidateAqlText.d.ts +21 -5
  49. package/dist/types-ts4.5/services/getAvailableJiraSites.d.ts +1 -5
  50. package/dist/types-ts4.5/ui/assets-modal/search-container/aql-search-input/index.d.ts +0 -1
  51. package/dist/types-ts4.5/ui/jira-issues-modal/basic-filters/hooks/useFilterOptions.d.ts +3 -2
  52. package/dist/types-ts4.5/ui/jira-issues-modal/basic-filters/ui/async-popup-select/index.d.ts +3 -2
  53. package/dist/types-ts4.5/ui/jira-issues-modal/basic-filters/ui/index.d.ts +3 -2
  54. package/dist/types-ts4.5/ui/jira-issues-modal/basic-filters/utils/transformers.d.ts +3 -1
  55. package/dist/types-ts4.5/ui/jira-issues-modal/jira-search-container/index.d.ts +2 -1
  56. package/dist/types-ts4.5/ui/jira-issues-modal/site-selector/index.d.ts +1 -1
  57. package/dist/types-ts4.5/ui/jira-issues-modal/types.d.ts +5 -0
  58. 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
- cloudId = _ref.cloudId,
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 currentSiteCloudId = (0, _react.useRef)(cloudId);
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
- cloudId: cloudId
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 _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(newSearchTerm, actionMeta) {
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 _ref2.apply(this, arguments);
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(_ref3) {
266
- var isOpen = _ref3.isOpen,
267
- triggerProps = (0, _objectWithoutProperties2.default)(_ref3, _excluded);
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
- cloudId = _ref.cloudId,
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
- cloudId: cloudId,
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) : null;
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
- cloudId: cloudId || '',
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,5 +1,5 @@
1
1
  export const EVENT_CHANNEL = 'media';
2
2
  export const packageMetaData = {
3
3
  packageName: "@atlaskit/link-datasource",
4
- packageVersion: "1.19.21"
4
+ packageVersion: "1.19.23"
5
5
  };
@@ -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 useValidateAqlText = workspaceId => {
5
- const [loading, setLoading] = useState(false);
6
- const [isValidAqlText, setIsValidAqlText] = useState(false);
7
- const [error, setError] = useState();
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
- setLoading(true);
13
- setError(undefined);
14
- let isValid = false;
15
- let message = null;
16
- try {
17
- var _validateAqlResponse$;
18
- const validateAqlResponse = await validateAql(workspaceId, {
19
- qlQuery: aql
20
- }, fireEvent);
21
- setIsValidAqlText(validateAqlResponse.isValid);
22
- isValid = validateAqlResponse.isValid;
23
- message = ((_validateAqlResponse$ = validateAqlResponse.errors) === null || _validateAqlResponse$ === void 0 ? void 0 : _validateAqlResponse$.iql) || null;
24
- } catch (err) {
25
- if (err instanceof Error) {
26
- setError(err);
27
- } else {
28
- setError(new Error('Unexpected error occured'));
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
- return {
34
- isValid,
35
- message
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
- isValidAqlText,
79
+ debouncedValidation,
40
80
  validateAqlText,
41
- validateAqlTextLoading: loading,
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(async (searchAql, searchSchemaId) => {
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
- if (isFetchingInitialData) {
78
- // Placing this check first as it's a priority before all others
79
- return jsx(LoadingView, null);
80
- } else if (status === 'rejected') {
81
- return jsx(RejectedView, null);
82
- } else if (status === 'unauthorized') {
83
- return jsx(UnauthorizedView, null);
84
- } else if (status === 'empty') {
85
- return jsx(EmptyView, null);
86
- } else if (resolvedWithNoResults) {
87
- return jsx(NoResultsView, null);
88
- } else if (status === 'loading' && !columns.length) {
89
- return jsx(LoadingView, null);
90
- }
91
- return issueLikeDataTableView;
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, useCallback, useRef, useState } from 'react';
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
- export const SEARCH_DEBOUNCE_MS = 350;
38
- const renderValidatorIcon = (value, error, meta) => {
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 (value && error) {
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 (value && meta.valid) {
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
- validateAqlText
82
- } = useValidateAqlText(workspaceId);
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: debouncedMemoizedValidation
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(fieldProps.value, error, meta)),
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: fieldProps.value.trim() === '' || meta.validating || !meta.valid
111
+ isDisabled: lastValidationResult.type !== 'valid'
155
112
  })),
156
113
  placeholder: formatMessage(searchInputMessages.placeholder),
157
114
  testId: testId
158
- })), fieldProps.value && error && message && jsx(ErrorMessage, null, message))));
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
- cloudId
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(response);
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
- cloudId,
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 currentSiteCloudId = useRef(cloudId);
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
- cloudId
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
- cloudId,
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
- cloudId: cloudId,
28
+ site: site,
26
29
  filterType: filter,
27
30
  key: filter,
28
31
  selection: selections[filter] || [],