@atlaskit/link-datasource 1.16.5 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
- import React, { useCallback, useEffect, useRef, useState } from 'react';
2
+ import React, { useCallback, useEffect, useState } from 'react';
3
3
  import { useIntl } from 'react-intl-next';
4
4
  import { useDebouncedCallback } from 'use-debounce';
5
5
  import { CheckboxOption, PopupSelect } from '@atlaskit/select';
@@ -24,9 +24,9 @@ const AsyncPopupSelect = ({
24
24
  const {
25
25
  formatMessage
26
26
  } = useIntl();
27
- const pickerRef = useRef(null);
28
27
  const [searchTerm, setSearchTerm] = useState('');
29
28
  const [selectedOptions, setSelectedOptions] = useState(selection);
29
+ const [sortedOptions, setSortedOptions] = useState(selectedOptions);
30
30
  const {
31
31
  filterOptions,
32
32
  fetchFilterOptions,
@@ -53,14 +53,32 @@ const AsyncPopupSelect = ({
53
53
  setSelectedOptions(newValue);
54
54
  onSelectionChange(newValue);
55
55
  };
56
- const handleOpenPopup = useCallback(() => {
57
- if (status === 'empty' || status === 'rejected') {
58
- // if user searches and gets status as rejected, we want the dropdown to try load the request with searchString when the user reopens the dropdown
59
- fetchFilterOptions({
60
- searchString: searchTerm
56
+ const sortOptionsOnPopupOpen = useCallback(() => {
57
+ if (selectedOptions.length === 0) {
58
+ setSortedOptions(filterOptions);
59
+ return;
60
+ }
61
+ const nonSelectedOptions = filterOptions.filter(option => !selectedOptions.find(selectedOption => selectedOption.value === option.value));
62
+ const newOptions = [...selectedOptions, ...nonSelectedOptions];
63
+ setSortedOptions(newOptions);
64
+ }, [selectedOptions, filterOptions]);
65
+ const sortOptionsOnResolve = useCallback(() => {
66
+ const newOptions = filterOptions.filter(option => !sortedOptions.find(sortedOption => sortedOption.value === option.value));
67
+ let shouldSetSortOptions = false;
68
+ if (sortedOptions.length !== filterOptions.length) {
69
+ shouldSetSortOptions = true;
70
+ } else {
71
+ sortedOptions.forEach(sortedOption => {
72
+ if (!filterOptions.some(filterOption => filterOption.value === sortedOption.value)) {
73
+ shouldSetSortOptions = true;
74
+ }
61
75
  });
62
76
  }
63
- }, [fetchFilterOptions, searchTerm, status]);
77
+ if (shouldSetSortOptions) {
78
+ const sortedOptionsFiltered = sortedOptions.filter(sortedOption => filterOptions.some(filterOption => filterOption.value === sortedOption.value));
79
+ setSortedOptions([...sortedOptionsFiltered, ...newOptions]);
80
+ }
81
+ }, [filterOptions, sortedOptions]);
64
82
  const handleShowMore = useCallback(() => {
65
83
  if (pageCursor) {
66
84
  fetchFilterOptions({
@@ -69,28 +87,34 @@ const AsyncPopupSelect = ({
69
87
  });
70
88
  }
71
89
  }, [fetchFilterOptions, pageCursor, searchTerm]);
90
+ const handleOpenPopup = useCallback(() => {
91
+ if (status === 'empty' || status === 'rejected') {
92
+ // if user searches and gets status as rejected, we want the dropdown to try load the request with searchString when the user reopens the dropdown
93
+ fetchFilterOptions({
94
+ searchString: searchTerm
95
+ });
96
+ } else if (status === 'resolved') {
97
+ sortOptionsOnPopupOpen();
98
+ }
99
+ }, [fetchFilterOptions, searchTerm, sortOptionsOnPopupOpen, status]);
72
100
  useEffect(() => {
73
101
  if (status === 'resolved') {
74
- var _pickerRef$current, _pickerRef$current$se, _pickerRef$current$se2;
75
- // necessary to refocus the search input after the loading state
76
- pickerRef === null || pickerRef === void 0 ? void 0 : (_pickerRef$current = pickerRef.current) === null || _pickerRef$current === void 0 ? void 0 : (_pickerRef$current$se = _pickerRef$current.selectRef) === null || _pickerRef$current$se === void 0 ? void 0 : (_pickerRef$current$se2 = _pickerRef$current$se.inputRef) === null || _pickerRef$current$se2 === void 0 ? void 0 : _pickerRef$current$se2.focus();
102
+ sortOptionsOnResolve();
77
103
  }
78
- }, [status]);
104
+ }, [sortOptionsOnResolve, status]);
79
105
  const filterOptionsLength = filterOptions.length;
80
106
  const isError = status === 'rejected';
81
107
  const isLoading = status === 'loading' || status === 'empty';
82
108
  const isLoadingMore = status === 'loadingMore';
83
109
  const isEmpty = status === 'resolved' && filterOptionsLength === 0;
110
+ const popupSelectOptions = isLoading || isError ? [] : sortedOptions; // if not set to [], then on loading, no loading UI will be shown
84
111
  const areAllResultsLoaded = filterOptions.length === totalCount;
85
112
  const shouldShowFooter = (status === 'resolved' || isLoadingMore) && filterOptions.length > 0; // footer should not disappear when there is an inline spinner for loading more data
86
113
  const shouldDisplayShowMoreButton = status === 'resolved' && !!pageCursor && !areAllResultsLoaded;
87
- const options = isLoading || isError ? [] : filterOptions; // if not set to [], for eg: on loading, no loading UI will be shown
88
-
89
114
  return /*#__PURE__*/React.createElement(PopupSelect, {
90
115
  isMulti: true,
91
116
  maxMenuWidth: 300,
92
117
  minMenuWidth: 300,
93
- ref: pickerRef,
94
118
  testId: "jlol-basic-filter-popup-select",
95
119
  inputId: "jlol-basic-filter-popup-select--input"
96
120
  /*
@@ -124,7 +148,7 @@ const AsyncPopupSelect = ({
124
148
  IndicatorSeparator: undefined // disables the | separator between search input and icon
125
149
  },
126
150
 
127
- options: options,
151
+ options: popupSelectOptions,
128
152
  value: selectedOptions,
129
153
  filterOption: noFilterOptions,
130
154
  formatOptionLabel: formatOptionLabel,
@@ -135,6 +159,7 @@ const AsyncPopupSelect = ({
135
159
  ...triggerProps
136
160
  }) => /*#__PURE__*/React.createElement(PopupTrigger, _extends({}, triggerProps, {
137
161
  filterType: filterType,
162
+ selectedOptions: selectedOptions,
138
163
  isSelected: isOpen,
139
164
  onClick: handleOpenPopup,
140
165
  isDisabled: isDisabled
@@ -1,24 +1,44 @@
1
1
  import React, { forwardRef } from 'react';
2
2
  import { FormattedMessage } from 'react-intl-next';
3
+ import Badge from '@atlaskit/badge';
3
4
  import Button from '@atlaskit/button/standard-button';
4
5
  import ChevronDownIcon from '@atlaskit/icon/glyph/chevron-down';
6
+ import { Box, Flex, xcss } from '@atlaskit/primitives';
5
7
  import { asyncPopupSelectMessages } from './messages';
8
+ const triggerButtonLabelStyles = xcss({
9
+ textOverflow: 'ellipsis',
10
+ overflow: 'hidden',
11
+ maxWidth: '150px'
12
+ });
13
+ const badgeStyles = xcss({
14
+ marginLeft: 'space.050'
15
+ });
6
16
  const PopupTrigger = /*#__PURE__*/forwardRef(({
7
17
  filterType,
8
18
  isSelected,
9
19
  isDisabled,
10
- onClick
20
+ onClick,
21
+ selectedOptions
11
22
  }, ref) => {
23
+ const [firstOption] = selectedOptions || [];
24
+ const hasOptions = selectedOptions && selectedOptions.length > 0;
12
25
  return /*#__PURE__*/React.createElement(Button, {
13
26
  ref: ref,
14
27
  appearance: "default",
15
- isSelected: isSelected,
28
+ isSelected: isSelected || hasOptions,
16
29
  isDisabled: isDisabled,
17
30
  onClick: onClick,
18
31
  testId: `jlol-basic-filter-${filterType}-trigger`,
19
32
  iconAfter: /*#__PURE__*/React.createElement(ChevronDownIcon, {
20
33
  label: ""
21
34
  })
22
- }, /*#__PURE__*/React.createElement(FormattedMessage, asyncPopupSelectMessages[`${filterType}Label`]));
35
+ }, /*#__PURE__*/React.createElement(Flex, null, /*#__PURE__*/React.createElement(Box, {
36
+ xcss: triggerButtonLabelStyles
37
+ }, /*#__PURE__*/React.createElement(FormattedMessage, asyncPopupSelectMessages[`${filterType}Label`]), firstOption && /*#__PURE__*/React.createElement(React.Fragment, null, ": ", firstOption.label)), selectedOptions && selectedOptions.length > 1 && /*#__PURE__*/React.createElement(Flex, {
38
+ xcss: badgeStyles,
39
+ alignItems: "center"
40
+ }, /*#__PURE__*/React.createElement(Badge, {
41
+ appearance: "primary"
42
+ }, "+", selectedOptions.length - 1))));
23
43
  });
24
44
  export default PopupTrigger;
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
2
  import { Flex, xcss } from '@atlaskit/primitives';
3
3
  import { isValidJql } from '../utils';
4
4
  import AsyncPopupSelect from './async-popup-select';
@@ -10,13 +10,15 @@ const BasicFilterContainer = ({
10
10
  jql,
11
11
  cloudId
12
12
  }) => {
13
- const [selection] = useState([]);
13
+ const [selection, setSelection] = useState([]);
14
14
  useEffect(() => {
15
15
  if (isValidJql(jql)) {
16
16
  // hydrate hook call goes in here
17
17
  }
18
18
  }, [jql]);
19
- const handleSelectionChange = () => {};
19
+ const handleSelectionChange = useCallback(options => {
20
+ setSelection(options);
21
+ }, [setSelection]);
20
22
  return /*#__PURE__*/React.createElement(Flex, {
21
23
  xcss: basicFilterContainerStyles,
22
24
  gap: "space.100",
@@ -1,5 +1,5 @@
1
1
  export var EVENT_CHANNEL = 'media';
2
2
  export var packageMetaData = {
3
3
  packageName: "@atlaskit/link-datasource",
4
- packageVersion: "1.16.5"
4
+ packageVersion: "1.17.0"
5
5
  };
@@ -13,6 +13,8 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
13
13
  fieldKeys = _ref$fieldKeys === void 0 ? [] : _ref$fieldKeys;
14
14
  var _useDatasourceAnalyti = useDatasourceAnalyticsEvents(),
15
15
  fireEvent = _useDatasourceAnalyti.fireEvent;
16
+ var idFieldCount = 1;
17
+ var keyFieldCount = 1;
16
18
  var _useState = useState([]),
17
19
  _useState2 = _slicedToArray(_useState, 2),
18
20
  defaultVisibleColumnKeys = _useState2[0],
@@ -21,42 +23,48 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
21
23
  _useState4 = _slicedToArray(_useState3, 2),
22
24
  lastRequestedFieldKeys = _useState4[0],
23
25
  setLastRequestedFieldKeys = _useState4[1];
24
- var _useState5 = useState('empty'),
26
+ var _useState5 = useState({
27
+ properties: []
28
+ }),
25
29
  _useState6 = _slicedToArray(_useState5, 2),
26
- status = _useState6[0],
27
- setStatus = _useState6[1];
28
- var _useState7 = useState([]),
30
+ fullSchema = _useState6[0],
31
+ setFullSchema = _useState6[1];
32
+ var _useState7 = useState('empty'),
29
33
  _useState8 = _slicedToArray(_useState7, 2),
30
- responseItems = _useState8[0],
31
- setResponseItems = _useState8[1];
32
- var _useState9 = useState(true),
34
+ status = _useState8[0],
35
+ setStatus = _useState8[1];
36
+ var _useState9 = useState([]),
33
37
  _useState10 = _slicedToArray(_useState9, 2),
34
- hasNextPage = _useState10[0],
35
- setHasNextPage = _useState10[1];
36
- var _useState11 = useState(undefined),
38
+ responseItems = _useState10[0],
39
+ setResponseItems = _useState10[1];
40
+ var _useState11 = useState(true),
37
41
  _useState12 = _slicedToArray(_useState11, 2),
38
- nextCursor = _useState12[0],
39
- setNextCursor = _useState12[1];
40
- var _useState13 = useState([]),
42
+ hasNextPage = _useState12[0],
43
+ setHasNextPage = _useState12[1];
44
+ var _useState13 = useState(undefined),
41
45
  _useState14 = _slicedToArray(_useState13, 2),
42
- columns = _useState14[0],
43
- setColumns = _useState14[1];
44
- var _useState15 = useState(undefined),
46
+ nextCursor = _useState14[0],
47
+ setNextCursor = _useState14[1];
48
+ var _useState15 = useState([]),
45
49
  _useState16 = _slicedToArray(_useState15, 2),
46
- totalCount = _useState16[0],
47
- setTotalCount = _useState16[1];
48
- var _useState17 = useState(false),
50
+ columns = _useState16[0],
51
+ setColumns = _useState16[1];
52
+ var _useState17 = useState(undefined),
49
53
  _useState18 = _slicedToArray(_useState17, 2),
50
- shouldForceRequest = _useState18[0],
51
- setShouldForceRequest = _useState18[1];
52
- var _useState19 = useState([]),
54
+ totalCount = _useState18[0],
55
+ setTotalCount = _useState18[1];
56
+ var _useState19 = useState(false),
53
57
  _useState20 = _slicedToArray(_useState19, 2),
54
- destinationObjectTypes = _useState20[0],
55
- setDestinationObjectTypes = _useState20[1];
56
- var _useState21 = useState(),
58
+ shouldForceRequest = _useState20[0],
59
+ setShouldForceRequest = _useState20[1];
60
+ var _useState21 = useState([]),
57
61
  _useState22 = _slicedToArray(_useState21, 2),
58
- extensionKey = _useState22[0],
59
- setExtensionKey = _useState22[1];
62
+ destinationObjectTypes = _useState22[0],
63
+ setDestinationObjectTypes = _useState22[1];
64
+ var _useState23 = useState(),
65
+ _useState24 = _slicedToArray(_useState23, 2),
66
+ extensionKey = _useState24[0],
67
+ setExtensionKey = _useState24[1];
60
68
  var _useDatasourceClientE = useDatasourceClientExtension(),
61
69
  getDatasourceData = _useDatasourceClientE.getDatasourceData,
62
70
  getDatasourceDetails = _useDatasourceClientE.getDatasourceDetails;
@@ -114,21 +122,39 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
114
122
  }
115
123
  }, _callee, null, [[2, 17]]);
116
124
  })), [columns, datasourceId, getDatasourceDetails, parameters]);
117
- var applySchemaProperties = useCallback(function (properties) {
118
- if (!isEqual(columns, properties)) {
119
- setColumns(properties);
125
+ var applySchemaProperties = useCallback(function (schema, fieldKeys) {
126
+ var properties = schema.properties,
127
+ _schema$defaultProper = schema.defaultProperties,
128
+ defaultProperties = _schema$defaultProper === void 0 ? [] : _schema$defaultProper;
129
+ var propertiesToBeUsed = properties;
130
+ var propertyKeysToBeUsed = Array.isArray(fieldKeys) && fieldKeys.length > 0 ? fieldKeys : defaultProperties;
131
+ if (fieldKeys.length > 0 || defaultProperties.length > 0) {
132
+ propertiesToBeUsed = properties.filter(function (property) {
133
+ return propertyKeysToBeUsed.includes(property.key);
134
+ });
135
+ }
136
+
137
+ /*Jira adds identifier fields like id and key to all data responses
138
+ Since defaultProperties already send back the keyField, we are accounting only
139
+ for the idField when we are using defaulProperties
140
+ */
141
+ if (properties.length > fieldKeys.length + idFieldCount + keyFieldCount && properties.length > defaultProperties.length + idFieldCount) {
142
+ setFullSchema(schema);
120
143
  }
121
- var defaultProperties = properties.map(function (prop) {
144
+ if (!isEqual(columns, propertiesToBeUsed)) {
145
+ setColumns(propertiesToBeUsed);
146
+ }
147
+ var newProperties = propertiesToBeUsed.map(function (prop) {
122
148
  return prop.key;
123
149
  });
124
150
 
125
151
  // when loading for the first time, we will need to set default visible props as /data does not give you that info
126
152
  // also, since we dont pass any fields, we will need to set this info as lastRequestedFieldKeys
127
- if (!isEqual(defaultVisibleColumnKeys, defaultProperties)) {
128
- setDefaultVisibleColumnKeys(defaultProperties);
153
+ if (!isEqual(defaultVisibleColumnKeys, newProperties)) {
154
+ setDefaultVisibleColumnKeys(newProperties);
129
155
  }
130
- if (!isEqual(lastRequestedFieldKeys, defaultProperties)) {
131
- setLastRequestedFieldKeys(defaultProperties);
156
+ if (!isEqual(lastRequestedFieldKeys, newProperties)) {
157
+ setLastRequestedFieldKeys(newProperties);
132
158
  }
133
159
  }, [columns, defaultVisibleColumnKeys, lastRequestedFieldKeys]);
134
160
  var onNextPage = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
@@ -138,6 +164,7 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
138
164
  shouldRequestFirstPage,
139
165
  _requestInfo$shouldFo,
140
166
  shouldForceRequest,
167
+ isFullSchemaLoaded,
141
168
  datasourceDataRequest,
142
169
  _yield$getDatasourceD2,
143
170
  _yield$getDatasourceD3,
@@ -164,18 +191,19 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
164
191
  return _context2.abrupt("return");
165
192
  case 3:
166
193
  _requestInfo$isSchema = requestInfo.isSchemaFromData, isSchemaFromData = _requestInfo$isSchema === void 0 ? true : _requestInfo$isSchema, shouldRequestFirstPage = requestInfo.shouldRequestFirstPage, _requestInfo$shouldFo = requestInfo.shouldForceRequest, shouldForceRequest = _requestInfo$shouldFo === void 0 ? false : _requestInfo$shouldFo;
194
+ isFullSchemaLoaded = fullSchema.properties.length > 0;
167
195
  datasourceDataRequest = {
168
196
  parameters: parameters,
169
197
  pageSize: DEFAULT_GET_DATASOURCE_DATA_PAGE_SIZE,
170
198
  pageCursor: shouldRequestFirstPage ? undefined : nextCursor,
171
199
  fields: fieldKeys,
172
- includeSchema: isSchemaFromData
200
+ includeSchema: isFullSchemaLoaded ? false : isSchemaFromData
173
201
  };
174
202
  setStatus('loading');
175
- _context2.prev = 6;
176
- _context2.next = 9;
203
+ _context2.prev = 7;
204
+ _context2.next = 10;
177
205
  return getDatasourceData(datasourceId, datasourceDataRequest, shouldForceRequest);
178
- case 9:
206
+ case 10:
179
207
  _yield$getDatasourceD2 = _context2.sent;
180
208
  _yield$getDatasourceD3 = _yield$getDatasourceD2.meta;
181
209
  access = _yield$getDatasourceD3.access;
@@ -187,12 +215,12 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
187
215
  _totalCount = _yield$getDatasourceD4.totalCount;
188
216
  schema = _yield$getDatasourceD4.schema;
189
217
  if (!(access === 'forbidden' || access === 'unauthorized')) {
190
- _context2.next = 22;
218
+ _context2.next = 23;
191
219
  break;
192
220
  }
193
221
  setStatus('unauthorized');
194
222
  return _context2.abrupt("return");
195
- case 22:
223
+ case 23:
196
224
  setExtensionKey(_extensionKey);
197
225
  setDestinationObjectTypes(_destinationObjectTypes);
198
226
  setTotalCount(_totalCount);
@@ -207,8 +235,8 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
207
235
  if (fieldKeys.length > 0) {
208
236
  setLastRequestedFieldKeys(fieldKeys);
209
237
  }
210
- if (isSchemaFromData && schema && items.length > 0) {
211
- applySchemaProperties(schema.properties);
238
+ if ((isSchemaFromData && schema || fullSchema.properties.length > 0) && items.length > 0) {
239
+ applySchemaProperties(schema || fullSchema, fieldKeys);
212
240
  }
213
241
  isUserLoadingNextPage = (responseItems === null || responseItems === void 0 ? void 0 : responseItems.length) !== 0 && !shouldRequestFirstPage;
214
242
  if (isUserLoadingNextPage) {
@@ -221,25 +249,25 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
221
249
  });
222
250
  }
223
251
  setStatus('resolved');
224
- _context2.next = 41;
252
+ _context2.next = 42;
225
253
  break;
226
- case 35:
227
- _context2.prev = 35;
228
- _context2.t0 = _context2["catch"](6);
254
+ case 36:
255
+ _context2.prev = 36;
256
+ _context2.t0 = _context2["catch"](7);
229
257
  if (!(_context2.t0 instanceof Response && (_context2.t0.status === 401 || _context2.t0.status === 403))) {
230
- _context2.next = 40;
258
+ _context2.next = 41;
231
259
  break;
232
260
  }
233
261
  setStatus('unauthorized');
234
262
  return _context2.abrupt("return");
235
- case 40:
236
- setStatus('rejected');
237
263
  case 41:
264
+ setStatus('rejected');
265
+ case 42:
238
266
  case "end":
239
267
  return _context2.stop();
240
268
  }
241
- }, _callee2, null, [[6, 35]]);
242
- })), [parameters, fieldKeys, nextCursor, getDatasourceData, datasourceId, responseItems === null || responseItems === void 0 ? void 0 : responseItems.length, applySchemaProperties, fireEvent]);
269
+ }, _callee2, null, [[7, 36]]);
270
+ })), [parameters, fieldKeys, nextCursor, getDatasourceData, datasourceId, responseItems === null || responseItems === void 0 ? void 0 : responseItems.length, applySchemaProperties, fireEvent, fullSchema]);
243
271
  var reset = useCallback(function (options) {
244
272
  setStatus('empty');
245
273
  setResponseItems([]);
@@ -247,6 +275,9 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
247
275
  setNextCursor(undefined);
248
276
  setTotalCount(undefined);
249
277
  setLastRequestedFieldKeys([]);
278
+ setFullSchema({
279
+ properties: []
280
+ });
250
281
  setShouldForceRequest((options === null || options === void 0 ? void 0 : options.shouldForceRequest) || false);
251
282
  if (options !== null && options !== void 0 && options.shouldResetColumns) {
252
283
  setColumns([]);
@@ -1,10 +1,11 @@
1
1
  import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
2
2
  import _extends from "@babel/runtime/helpers/extends";
3
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
4
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
4
5
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
5
6
  var _excluded = ["isOpen"];
6
7
  import _regeneratorRuntime from "@babel/runtime/regenerator";
7
- import React, { useCallback, useEffect, useRef, useState } from 'react';
8
+ import React, { useCallback, useEffect, useState } from 'react';
8
9
  import { useIntl } from 'react-intl-next';
9
10
  import { useDebouncedCallback } from 'use-debounce';
10
11
  import { CheckboxOption, PopupSelect } from '@atlaskit/select';
@@ -31,7 +32,6 @@ var AsyncPopupSelect = function AsyncPopupSelect(_ref) {
31
32
  isDisabled = _ref$isDisabled === void 0 ? false : _ref$isDisabled;
32
33
  var _useIntl = useIntl(),
33
34
  formatMessage = _useIntl.formatMessage;
34
- var pickerRef = useRef(null);
35
35
  var _useState = useState(''),
36
36
  _useState2 = _slicedToArray(_useState, 2),
37
37
  searchTerm = _useState2[0],
@@ -40,6 +40,10 @@ var AsyncPopupSelect = function AsyncPopupSelect(_ref) {
40
40
  _useState4 = _slicedToArray(_useState3, 2),
41
41
  selectedOptions = _useState4[0],
42
42
  setSelectedOptions = _useState4[1];
43
+ var _useState5 = useState(selectedOptions),
44
+ _useState6 = _slicedToArray(_useState5, 2),
45
+ sortedOptions = _useState6[0],
46
+ setSortedOptions = _useState6[1];
43
47
  var _useFilterOptions = useFilterOptions({
44
48
  filterType: filterType,
45
49
  cloudId: cloudId
@@ -80,14 +84,46 @@ var AsyncPopupSelect = function AsyncPopupSelect(_ref) {
80
84
  setSelectedOptions(newValue);
81
85
  onSelectionChange(newValue);
82
86
  };
83
- var handleOpenPopup = useCallback(function () {
84
- if (status === 'empty' || status === 'rejected') {
85
- // if user searches and gets status as rejected, we want the dropdown to try load the request with searchString when the user reopens the dropdown
86
- fetchFilterOptions({
87
- searchString: searchTerm
87
+ var sortOptionsOnPopupOpen = useCallback(function () {
88
+ if (selectedOptions.length === 0) {
89
+ setSortedOptions(filterOptions);
90
+ return;
91
+ }
92
+ var nonSelectedOptions = filterOptions.filter(function (option) {
93
+ return !selectedOptions.find(function (selectedOption) {
94
+ return selectedOption.value === option.value;
95
+ });
96
+ });
97
+ var newOptions = [].concat(_toConsumableArray(selectedOptions), _toConsumableArray(nonSelectedOptions));
98
+ setSortedOptions(newOptions);
99
+ }, [selectedOptions, filterOptions]);
100
+ var sortOptionsOnResolve = useCallback(function () {
101
+ var newOptions = filterOptions.filter(function (option) {
102
+ return !sortedOptions.find(function (sortedOption) {
103
+ return sortedOption.value === option.value;
104
+ });
105
+ });
106
+ var shouldSetSortOptions = false;
107
+ if (sortedOptions.length !== filterOptions.length) {
108
+ shouldSetSortOptions = true;
109
+ } else {
110
+ sortedOptions.forEach(function (sortedOption) {
111
+ if (!filterOptions.some(function (filterOption) {
112
+ return filterOption.value === sortedOption.value;
113
+ })) {
114
+ shouldSetSortOptions = true;
115
+ }
88
116
  });
89
117
  }
90
- }, [fetchFilterOptions, searchTerm, status]);
118
+ if (shouldSetSortOptions) {
119
+ var sortedOptionsFiltered = sortedOptions.filter(function (sortedOption) {
120
+ return filterOptions.some(function (filterOption) {
121
+ return filterOption.value === sortedOption.value;
122
+ });
123
+ });
124
+ setSortedOptions([].concat(_toConsumableArray(sortedOptionsFiltered), _toConsumableArray(newOptions)));
125
+ }
126
+ }, [filterOptions, sortedOptions]);
91
127
  var handleShowMore = useCallback(function () {
92
128
  if (pageCursor) {
93
129
  fetchFilterOptions({
@@ -96,28 +132,34 @@ var AsyncPopupSelect = function AsyncPopupSelect(_ref) {
96
132
  });
97
133
  }
98
134
  }, [fetchFilterOptions, pageCursor, searchTerm]);
135
+ var handleOpenPopup = useCallback(function () {
136
+ if (status === 'empty' || status === 'rejected') {
137
+ // if user searches and gets status as rejected, we want the dropdown to try load the request with searchString when the user reopens the dropdown
138
+ fetchFilterOptions({
139
+ searchString: searchTerm
140
+ });
141
+ } else if (status === 'resolved') {
142
+ sortOptionsOnPopupOpen();
143
+ }
144
+ }, [fetchFilterOptions, searchTerm, sortOptionsOnPopupOpen, status]);
99
145
  useEffect(function () {
100
146
  if (status === 'resolved') {
101
- var _pickerRef$current;
102
- // necessary to refocus the search input after the loading state
103
- pickerRef === null || pickerRef === void 0 || (_pickerRef$current = pickerRef.current) === null || _pickerRef$current === void 0 || (_pickerRef$current = _pickerRef$current.selectRef) === null || _pickerRef$current === void 0 || (_pickerRef$current = _pickerRef$current.inputRef) === null || _pickerRef$current === void 0 || _pickerRef$current.focus();
147
+ sortOptionsOnResolve();
104
148
  }
105
- }, [status]);
149
+ }, [sortOptionsOnResolve, status]);
106
150
  var filterOptionsLength = filterOptions.length;
107
151
  var isError = status === 'rejected';
108
152
  var isLoading = status === 'loading' || status === 'empty';
109
153
  var isLoadingMore = status === 'loadingMore';
110
154
  var isEmpty = status === 'resolved' && filterOptionsLength === 0;
155
+ var popupSelectOptions = isLoading || isError ? [] : sortedOptions; // if not set to [], then on loading, no loading UI will be shown
111
156
  var areAllResultsLoaded = filterOptions.length === totalCount;
112
157
  var shouldShowFooter = (status === 'resolved' || isLoadingMore) && filterOptions.length > 0; // footer should not disappear when there is an inline spinner for loading more data
113
158
  var shouldDisplayShowMoreButton = status === 'resolved' && !!pageCursor && !areAllResultsLoaded;
114
- var options = isLoading || isError ? [] : filterOptions; // if not set to [], for eg: on loading, no loading UI will be shown
115
-
116
159
  return /*#__PURE__*/React.createElement(PopupSelect, {
117
160
  isMulti: true,
118
161
  maxMenuWidth: 300,
119
162
  minMenuWidth: 300,
120
- ref: pickerRef,
121
163
  testId: "jlol-basic-filter-popup-select",
122
164
  inputId: "jlol-basic-filter-popup-select--input"
123
165
  /*
@@ -153,7 +195,7 @@ var AsyncPopupSelect = function AsyncPopupSelect(_ref) {
153
195
  IndicatorSeparator: undefined // disables the | separator between search input and icon
154
196
  },
155
197
 
156
- options: options,
198
+ options: popupSelectOptions,
157
199
  value: selectedOptions,
158
200
  filterOption: noFilterOptions,
159
201
  formatOptionLabel: formatOptionLabel,
@@ -164,6 +206,7 @@ var AsyncPopupSelect = function AsyncPopupSelect(_ref) {
164
206
  triggerProps = _objectWithoutProperties(_ref3, _excluded);
165
207
  return /*#__PURE__*/React.createElement(PopupTrigger, _extends({}, triggerProps, {
166
208
  filterType: filterType,
209
+ selectedOptions: selectedOptions,
167
210
  isSelected: isOpen,
168
211
  onClick: handleOpenPopup,
169
212
  isDisabled: isDisabled
@@ -1,23 +1,46 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
1
2
  import React, { forwardRef } from 'react';
2
3
  import { FormattedMessage } from 'react-intl-next';
4
+ import Badge from '@atlaskit/badge';
3
5
  import Button from '@atlaskit/button/standard-button';
4
6
  import ChevronDownIcon from '@atlaskit/icon/glyph/chevron-down';
7
+ import { Box, Flex, xcss } from '@atlaskit/primitives';
5
8
  import { asyncPopupSelectMessages } from './messages';
9
+ var triggerButtonLabelStyles = xcss({
10
+ textOverflow: 'ellipsis',
11
+ overflow: 'hidden',
12
+ maxWidth: '150px'
13
+ });
14
+ var badgeStyles = xcss({
15
+ marginLeft: 'space.050'
16
+ });
6
17
  var PopupTrigger = /*#__PURE__*/forwardRef(function (_ref, ref) {
7
18
  var filterType = _ref.filterType,
8
19
  isSelected = _ref.isSelected,
9
20
  isDisabled = _ref.isDisabled,
10
- onClick = _ref.onClick;
21
+ onClick = _ref.onClick,
22
+ selectedOptions = _ref.selectedOptions;
23
+ var _ref2 = selectedOptions || [],
24
+ _ref3 = _slicedToArray(_ref2, 1),
25
+ firstOption = _ref3[0];
26
+ var hasOptions = selectedOptions && selectedOptions.length > 0;
11
27
  return /*#__PURE__*/React.createElement(Button, {
12
28
  ref: ref,
13
29
  appearance: "default",
14
- isSelected: isSelected,
30
+ isSelected: isSelected || hasOptions,
15
31
  isDisabled: isDisabled,
16
32
  onClick: onClick,
17
33
  testId: "jlol-basic-filter-".concat(filterType, "-trigger"),
18
34
  iconAfter: /*#__PURE__*/React.createElement(ChevronDownIcon, {
19
35
  label: ""
20
36
  })
21
- }, /*#__PURE__*/React.createElement(FormattedMessage, asyncPopupSelectMessages["".concat(filterType, "Label")]));
37
+ }, /*#__PURE__*/React.createElement(Flex, null, /*#__PURE__*/React.createElement(Box, {
38
+ xcss: triggerButtonLabelStyles
39
+ }, /*#__PURE__*/React.createElement(FormattedMessage, asyncPopupSelectMessages["".concat(filterType, "Label")]), firstOption && /*#__PURE__*/React.createElement(React.Fragment, null, ": ", firstOption.label)), selectedOptions && selectedOptions.length > 1 && /*#__PURE__*/React.createElement(Flex, {
40
+ xcss: badgeStyles,
41
+ alignItems: "center"
42
+ }, /*#__PURE__*/React.createElement(Badge, {
43
+ appearance: "primary"
44
+ }, "+", selectedOptions.length - 1))));
22
45
  });
23
46
  export default PopupTrigger;
@@ -1,5 +1,5 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
- import React, { useEffect, useState } from 'react';
2
+ import React, { useCallback, useEffect, useState } from 'react';
3
3
  import { Flex, xcss } from '@atlaskit/primitives';
4
4
  import { isValidJql } from '../utils';
5
5
  import AsyncPopupSelect from './async-popup-select';
@@ -11,14 +11,17 @@ var BasicFilterContainer = function BasicFilterContainer(_ref) {
11
11
  var jql = _ref.jql,
12
12
  cloudId = _ref.cloudId;
13
13
  var _useState = useState([]),
14
- _useState2 = _slicedToArray(_useState, 1),
15
- selection = _useState2[0];
14
+ _useState2 = _slicedToArray(_useState, 2),
15
+ selection = _useState2[0],
16
+ setSelection = _useState2[1];
16
17
  useEffect(function () {
17
18
  if (isValidJql(jql)) {
18
19
  // hydrate hook call goes in here
19
20
  }
20
21
  }, [jql]);
21
- var handleSelectionChange = function handleSelectionChange() {};
22
+ var handleSelectionChange = useCallback(function (options) {
23
+ setSelection(options);
24
+ }, [setSelection]);
22
25
  return /*#__PURE__*/React.createElement(Flex, {
23
26
  xcss: basicFilterContainerStyles,
24
27
  gap: "space.100",
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
- import { BasicFilterFieldType } from '../../types';
2
+ import { BasicFilterFieldType, SelectOption } from '../../types';
3
3
  export interface PopupTriggerProps {
4
4
  filterType: BasicFilterFieldType;
5
+ selectedOptions?: ReadonlyArray<SelectOption>;
5
6
  isSelected?: boolean;
6
7
  isDisabled?: boolean;
7
8
  onClick?: () => void;