@atlaskit/smart-user-picker 8.7.0 → 8.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @atlassian/smart-user-picker
2
2
 
3
+ ## 8.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`1d3de95b5af00`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/1d3de95b5af00) -
8
+ Refactor verifiedTeams filter
9
+
10
+ ## 8.8.0
11
+
12
+ ### Minor Changes
13
+
14
+ - [`386987f274ff5`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/386987f274ff5) -
15
+ Add support for `restrictTo` prop to filter recommendations by user IDs and group IDs. This allows
16
+ filtering down results based on specific groups or users in a site.
17
+
18
+ **New prop:** `restrictTo?: { userIds?: string[], groupIds?: string[] }`
19
+
20
+ This prop is passed directly to the URS recommendations endpoint's `searchQuery.restrictTo` field,
21
+ enabling you to constrain recommendations to a specific set of users and/or groups.
22
+
23
+ **Feature gate:** `smart-user-picker-restrict-to-gate` - The prop is only forwarded to the backend
24
+ when this feature gate is enabled.
25
+
3
26
  ## 8.7.0
4
27
 
5
28
  ### Minor Changes
@@ -34,11 +34,11 @@ var _service = require("../service");
34
34
  var _ufoExperiences = require("../ufoExperiences");
35
35
  var _excluded = ["allowEmail", "enableEmailSearch", "allowEmailSelectionWhenEmailMatched"];
36
36
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
37
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
38
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
39
37
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
40
38
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
41
39
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
40
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
41
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
42
42
  function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
43
43
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
44
44
  var DEFAULT_DEBOUNCE_TIME_MS = 150;
@@ -118,16 +118,16 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
118
118
  });
119
119
  (0, _defineProperty2.default)(_this, "memoizedFilterOptions", (0, _memoizeOne.default)(_this.filterOptions));
120
120
  (0, _defineProperty2.default)(_this, "getUsers", (0, _debounce.default)( /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
121
- var _this$state, query, sessionId, closed, _this$props, baseUrl, childObjectId, containerId, fieldId, includeGroups, includeTeams, includeTeamsUpdates, includeUsers, includeNonLicensedUsers, intl, fetchOptions, maxOptions, objectId, onEmpty, onError, overrideByline, displayEmailInByline, verifiedTeams, orgId, principalId, productAttributes, productKey, searchQueryFilter, siteId, transformOptions, userResolvers, enableEmailSearch, maxNumberOfResults, startTime, isEmail, recommendationsRequest, _yield$onEmpty, _query, recommendedUsers, userRecommendationsPromise, userResolversPromises, _yield$Promise$all, _yield$Promise$all2, mainRecommendations, userResolverResults, _iterator, _step, option, _iterator2, _step2, _option, _iterator3, _step3, _option2, elapsedTimeMilli, transformedOptions, displayedUsers, is5xxEvent, onErrorProducedError, defaultUsers, _elapsedTimeMilli;
121
+ var _this$state, query, sessionId, closed, _this$props, baseUrl, childObjectId, containerId, fieldId, includeGroups, includeTeams, includeTeamsUpdates, includeUsers, includeNonLicensedUsers, intl, fetchOptions, maxOptions, objectId, onEmpty, onError, overrideByline, displayEmailInByline, verifiedTeams, orgId, principalId, productAttributes, productKey, restrictTo, searchQueryFilter, siteId, transformOptions, userResolvers, enableEmailSearch, maxNumberOfResults, startTime, isEmail, recommendationsRequest, _yield$onEmpty, _query, recommendedUsers, userRecommendationsPromise, userResolversPromises, _yield$Promise$all, _yield$Promise$all2, mainRecommendations, userResolverResults, _iterator, _step, option, _iterator2, _step2, _option, _iterator3, _step3, _option2, elapsedTimeMilli, transformedOptions, displayedUsers, is5xxEvent, onErrorProducedError, defaultUsers, _elapsedTimeMilli;
122
122
  return _regenerator.default.wrap(function _callee$(_context) {
123
123
  while (1) switch (_context.prev = _context.next) {
124
124
  case 0:
125
125
  _this$state = _this.state, query = _this$state.query, sessionId = _this$state.sessionId, closed = _this$state.closed;
126
- _this$props = _this.props, baseUrl = _this$props.baseUrl, childObjectId = _this$props.childObjectId, containerId = _this$props.containerId, fieldId = _this$props.fieldId, includeGroups = _this$props.includeGroups, includeTeams = _this$props.includeTeams, includeTeamsUpdates = _this$props.includeTeamsUpdates, includeUsers = _this$props.includeUsers, includeNonLicensedUsers = _this$props.includeNonLicensedUsers, intl = _this$props.intl, fetchOptions = _this$props.fetchOptions, maxOptions = _this$props.maxOptions, objectId = _this$props.objectId, onEmpty = _this$props.onEmpty, onError = _this$props.onError, overrideByline = _this$props.overrideByline, displayEmailInByline = _this$props.displayEmailInByline, verifiedTeams = _this$props.verifiedTeams, orgId = _this$props.orgId, principalId = _this$props.principalId, productAttributes = _this$props.productAttributes, productKey = _this$props.productKey, searchQueryFilter = _this$props.searchQueryFilter, siteId = _this$props.siteId, transformOptions = _this$props.transformOptions, userResolvers = _this$props.userResolvers, enableEmailSearch = _this$props.enableEmailSearch;
126
+ _this$props = _this.props, baseUrl = _this$props.baseUrl, childObjectId = _this$props.childObjectId, containerId = _this$props.containerId, fieldId = _this$props.fieldId, includeGroups = _this$props.includeGroups, includeTeams = _this$props.includeTeams, includeTeamsUpdates = _this$props.includeTeamsUpdates, includeUsers = _this$props.includeUsers, includeNonLicensedUsers = _this$props.includeNonLicensedUsers, intl = _this$props.intl, fetchOptions = _this$props.fetchOptions, maxOptions = _this$props.maxOptions, objectId = _this$props.objectId, onEmpty = _this$props.onEmpty, onError = _this$props.onError, overrideByline = _this$props.overrideByline, displayEmailInByline = _this$props.displayEmailInByline, verifiedTeams = _this$props.verifiedTeams, orgId = _this$props.orgId, principalId = _this$props.principalId, productAttributes = _this$props.productAttributes, productKey = _this$props.productKey, restrictTo = _this$props.restrictTo, searchQueryFilter = _this$props.searchQueryFilter, siteId = _this$props.siteId, transformOptions = _this$props.transformOptions, userResolvers = _this$props.userResolvers, enableEmailSearch = _this$props.enableEmailSearch;
127
127
  maxNumberOfResults = maxOptions || 100;
128
128
  startTime = window.performance.now(); // Check if this is an email search
129
129
  isEmail = enableEmailSearch && isEmailQuery(query);
130
- recommendationsRequest = {
130
+ recommendationsRequest = _objectSpread({
131
131
  baseUrl: baseUrl,
132
132
  context: {
133
133
  containerId: containerId,
@@ -149,7 +149,8 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
149
149
  maxNumberOfResults: maxNumberOfResults,
150
150
  query: query,
151
151
  searchEmail: isEmail,
152
- verifiedTeams: verifiedTeams,
152
+ // Only request verified teams when both the prop is true and the feature flag is enabled
153
+ verifiedTeams: verifiedTeams && (0, _platformFeatureFlags.fg)('smart-user-picker-managed-teams-gate'),
153
154
  /*
154
155
  For email-based searches, we have decided to filter out apps.
155
156
  Also - because the other 2 filters ((NOT not_mentionable:true) AND (account_status:active)) are included
@@ -157,7 +158,9 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
157
158
  Further ref: https://developer.atlassian.com/platform/user-recommendations/guides/frequently-asked-questions/#filter-behavior
158
159
  */
159
160
  searchQueryFilter: isEmail && !searchQueryFilter ? '(NOT not_mentionable:true) AND (account_status:active) AND (NOT account_type:app)' : searchQueryFilter
160
- };
161
+ }, restrictTo && (0, _platformFeatureFlags.fg)('smart-user-picker-restrict-to-gate') && {
162
+ restrictTo: restrictTo
163
+ });
161
164
  _context.prev = 6;
162
165
  _query = _this.state.query;
163
166
  _this.fireEvent(_analytics.requestUsersEvent);
@@ -253,19 +256,6 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
253
256
  }
254
257
  }
255
258
 
256
- // Filter to only verified teams when verifiedTeams is true and feature flag is enabled
257
- if (verifiedTeams && includeTeams && (0, _platformFeatureFlags.fg)('smart-user-picker-managed-teams-gate')) {
258
- recommendedUsers = recommendedUsers.filter(function (option) {
259
- if ((0, _userPicker.isTeam)(option)) {
260
- // Only include teams that are verified
261
- // The verified property is set by the transformer from the server response
262
- var team = option;
263
- return team.verified === true;
264
- }
265
- return true; // Keep non-team options
266
- });
267
- }
268
-
269
259
  // Track if email search found matches for conditional allowEmail logic
270
260
  if (isEmail) {
271
261
  _this.lastEmailSearchFoundMatches = recommendedUsers.length > 0;
@@ -274,50 +264,50 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
274
264
  }
275
265
  elapsedTimeMilli = window.performance.now() - startTime;
276
266
  if (!transformOptions) {
277
- _context.next = 42;
267
+ _context.next = 41;
278
268
  break;
279
269
  }
280
- _context.next = 39;
270
+ _context.next = 38;
281
271
  return transformOptions(recommendedUsers, _query);
282
- case 39:
272
+ case 38:
283
273
  _context.t0 = _context.sent;
284
- _context.next = 43;
274
+ _context.next = 42;
285
275
  break;
286
- case 42:
276
+ case 41:
287
277
  _context.t0 = recommendedUsers;
288
- case 43:
278
+ case 42:
289
279
  transformedOptions = _context.t0;
290
280
  if (!(transformedOptions.length === 0 && onEmpty)) {
291
- _context.next = 59;
281
+ _context.next = 58;
292
282
  break;
293
283
  }
294
- _context.next = 47;
284
+ _context.next = 46;
295
285
  return onEmpty(_query);
296
- case 47:
286
+ case 46:
297
287
  _context.t3 = _yield$onEmpty = _context.sent;
298
288
  _context.t2 = _context.t3 !== null;
299
289
  if (!_context.t2) {
300
- _context.next = 51;
290
+ _context.next = 50;
301
291
  break;
302
292
  }
303
293
  _context.t2 = _yield$onEmpty !== void 0;
304
- case 51:
294
+ case 50:
305
295
  if (!_context.t2) {
306
- _context.next = 55;
296
+ _context.next = 54;
307
297
  break;
308
298
  }
309
299
  _context.t4 = _yield$onEmpty;
310
- _context.next = 56;
300
+ _context.next = 55;
311
301
  break;
312
- case 55:
302
+ case 54:
313
303
  _context.t4 = [];
314
- case 56:
304
+ case 55:
315
305
  _context.t1 = _context.t4;
316
- _context.next = 60;
306
+ _context.next = 59;
317
307
  break;
318
- case 59:
308
+ case 58:
319
309
  _context.t1 = transformedOptions;
320
- case 60:
310
+ case 59:
321
311
  displayedUsers = _context.t1;
322
312
  _this.setState(function (state) {
323
313
  var applicable = state.query === _query;
@@ -339,10 +329,10 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
339
329
  loading: loading
340
330
  };
341
331
  });
342
- _context.next = 93;
332
+ _context.next = 92;
343
333
  break;
344
- case 64:
345
- _context.prev = 64;
334
+ case 63:
335
+ _context.prev = 63;
346
336
  _context.t5 = _context["catch"](6);
347
337
  is5xxEvent = checkIf500Event(_context.t5.statusCode);
348
338
  if (!closed && !onError && is5xxEvent) {
@@ -355,35 +345,35 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
355
345
  });
356
346
  onErrorProducedError = false;
357
347
  defaultUsers = [];
358
- _context.prev = 71;
348
+ _context.prev = 70;
359
349
  if (!onError) {
360
- _context.next = 81;
350
+ _context.next = 80;
361
351
  break;
362
352
  }
363
- _context.next = 75;
353
+ _context.next = 74;
364
354
  return onError(_context.t5, recommendationsRequest);
365
- case 75:
355
+ case 74:
366
356
  _context.t7 = _context.sent;
367
357
  if (_context.t7) {
368
- _context.next = 78;
358
+ _context.next = 77;
369
359
  break;
370
360
  }
371
361
  _context.t7 = [];
372
- case 78:
362
+ case 77:
373
363
  _context.t6 = _context.t7;
374
- _context.next = 82;
364
+ _context.next = 81;
375
365
  break;
376
- case 81:
366
+ case 80:
377
367
  _context.t6 = [];
378
- case 82:
368
+ case 81:
379
369
  defaultUsers = _context.t6;
380
- _context.next = 88;
370
+ _context.next = 87;
381
371
  break;
382
- case 85:
383
- _context.prev = 85;
384
- _context.t8 = _context["catch"](71);
372
+ case 84:
373
+ _context.prev = 84;
374
+ _context.t8 = _context["catch"](70);
385
375
  onErrorProducedError = true;
386
- case 88:
376
+ case 87:
387
377
  if (onErrorProducedError && is5xxEvent) {
388
378
  // Log error from fallback data source `onError` to UFO
389
379
  _this.optionsShownUfoExperienceInstance.failure(ufoEndStateConfig(_this.props.fieldId));
@@ -400,11 +390,11 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
400
390
  elapsedTimeMilli: _elapsedTimeMilli,
401
391
  productAttributes: productAttributes
402
392
  });
403
- case 93:
393
+ case 92:
404
394
  case "end":
405
395
  return _context.stop();
406
396
  }
407
- }, _callee, null, [[6, 64], [71, 85]]);
397
+ }, _callee, null, [[6, 63], [70, 84]]);
408
398
  })), (_this$props$debounceT = _this.props.debounceTime) !== null && _this$props$debounceT !== void 0 ? _this$props$debounceT : 0));
409
399
  (0, _defineProperty2.default)(_this, "onInputChange", function (newQuery, sessionId) {
410
400
  var query = newQuery || '';
@@ -27,6 +27,7 @@ var getUserRecommendations = function getUserRecommendations(request, intl) {
27
27
  includeNonLicensedUsers: request.includeNonLicensedUsers,
28
28
  maxNumberOfResults: request.maxNumberOfResults,
29
29
  performSearchQueryOnly: false,
30
+ isVerifiedTeamFilter: request.verifiedTeams,
30
31
  searchQuery: _objectSpread(_objectSpread({
31
32
  cpusQueryHighlights: {
32
33
  query: '',
@@ -40,7 +41,7 @@ var getUserRecommendations = function getUserRecommendations(request, intl) {
40
41
  filter: request.searchQueryFilter || '',
41
42
  minimumAccessLevel: 'APPLICATION',
42
43
  queryString: request.query,
43
- restrictTo: {
44
+ restrictTo: request.restrictTo || {
44
45
  userIds: [],
45
46
  groupIds: []
46
47
  },
@@ -108,6 +108,7 @@ export class SmartUserPickerWithoutAnalytics extends React.Component {
108
108
  principalId,
109
109
  productAttributes,
110
110
  productKey,
111
+ restrictTo,
111
112
  searchQueryFilter,
112
113
  siteId,
113
114
  transformOptions,
@@ -141,14 +142,18 @@ export class SmartUserPickerWithoutAnalytics extends React.Component {
141
142
  maxNumberOfResults,
142
143
  query,
143
144
  searchEmail: isEmail,
144
- verifiedTeams,
145
+ // Only request verified teams when both the prop is true and the feature flag is enabled
146
+ verifiedTeams: verifiedTeams && fg('smart-user-picker-managed-teams-gate'),
145
147
  /*
146
148
  For email-based searches, we have decided to filter out apps.
147
149
  Also - because the other 2 filters ((NOT not_mentionable:true) AND (account_status:active)) are included
148
150
  when filter is empty, they have been added here to maintain consistency.
149
151
  Further ref: https://developer.atlassian.com/platform/user-recommendations/guides/frequently-asked-questions/#filter-behavior
150
152
  */
151
- searchQueryFilter: isEmail && !searchQueryFilter ? '(NOT not_mentionable:true) AND (account_status:active) AND (NOT account_type:app)' : searchQueryFilter
153
+ searchQueryFilter: isEmail && !searchQueryFilter ? '(NOT not_mentionable:true) AND (account_status:active) AND (NOT account_type:app)' : searchQueryFilter,
154
+ ...(restrictTo && fg('smart-user-picker-restrict-to-gate') && {
155
+ restrictTo
156
+ })
152
157
  };
153
158
  try {
154
159
  var _await$onEmpty;
@@ -200,19 +205,6 @@ export class SmartUserPickerWithoutAnalytics extends React.Component {
200
205
  }
201
206
  }
202
207
 
203
- // Filter to only verified teams when verifiedTeams is true and feature flag is enabled
204
- if (verifiedTeams && includeTeams && fg('smart-user-picker-managed-teams-gate')) {
205
- recommendedUsers = recommendedUsers.filter(option => {
206
- if (isTeam(option)) {
207
- // Only include teams that are verified
208
- // The verified property is set by the transformer from the server response
209
- const team = option;
210
- return team.verified === true;
211
- }
212
- return true; // Keep non-team options
213
- });
214
- }
215
-
216
208
  // Track if email search found matches for conditional allowEmail logic
217
209
  if (isEmail) {
218
210
  this.lastEmailSearchFoundMatches = recommendedUsers.length > 0;
@@ -17,6 +17,7 @@ const getUserRecommendations = (request, intl) => {
17
17
  includeNonLicensedUsers: request.includeNonLicensedUsers,
18
18
  maxNumberOfResults: request.maxNumberOfResults,
19
19
  performSearchQueryOnly: false,
20
+ isVerifiedTeamFilter: request.verifiedTeams,
20
21
  searchQuery: {
21
22
  cpusQueryHighlights: {
22
23
  query: '',
@@ -30,7 +31,7 @@ const getUserRecommendations = (request, intl) => {
30
31
  filter: request.searchQueryFilter || '',
31
32
  minimumAccessLevel: 'APPLICATION',
32
33
  queryString: request.query,
33
- restrictTo: {
34
+ restrictTo: request.restrictTo || {
34
35
  userIds: [],
35
36
  groupIds: []
36
37
  },
@@ -11,11 +11,11 @@ import _inherits from "@babel/runtime/helpers/inherits";
11
11
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
12
12
  var _excluded = ["allowEmail", "enableEmailSearch", "allowEmailSelectionWhenEmailMatched"];
13
13
  import _regeneratorRuntime from "@babel/runtime/regenerator";
14
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
15
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
16
14
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
17
15
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
18
16
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
17
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
18
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
19
19
  function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
20
20
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
21
21
  import React from 'react';
@@ -110,16 +110,16 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
110
110
  });
111
111
  _defineProperty(_this, "memoizedFilterOptions", memoizeOne(_this.filterOptions));
112
112
  _defineProperty(_this, "getUsers", debounce( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
113
- var _this$state, query, sessionId, closed, _this$props, baseUrl, childObjectId, containerId, fieldId, includeGroups, includeTeams, includeTeamsUpdates, includeUsers, includeNonLicensedUsers, intl, fetchOptions, maxOptions, objectId, onEmpty, onError, overrideByline, displayEmailInByline, verifiedTeams, orgId, principalId, productAttributes, productKey, searchQueryFilter, siteId, transformOptions, userResolvers, enableEmailSearch, maxNumberOfResults, startTime, isEmail, recommendationsRequest, _yield$onEmpty, _query, recommendedUsers, userRecommendationsPromise, userResolversPromises, _yield$Promise$all, _yield$Promise$all2, mainRecommendations, userResolverResults, _iterator, _step, option, _iterator2, _step2, _option, _iterator3, _step3, _option2, elapsedTimeMilli, transformedOptions, displayedUsers, is5xxEvent, onErrorProducedError, defaultUsers, _elapsedTimeMilli;
113
+ var _this$state, query, sessionId, closed, _this$props, baseUrl, childObjectId, containerId, fieldId, includeGroups, includeTeams, includeTeamsUpdates, includeUsers, includeNonLicensedUsers, intl, fetchOptions, maxOptions, objectId, onEmpty, onError, overrideByline, displayEmailInByline, verifiedTeams, orgId, principalId, productAttributes, productKey, restrictTo, searchQueryFilter, siteId, transformOptions, userResolvers, enableEmailSearch, maxNumberOfResults, startTime, isEmail, recommendationsRequest, _yield$onEmpty, _query, recommendedUsers, userRecommendationsPromise, userResolversPromises, _yield$Promise$all, _yield$Promise$all2, mainRecommendations, userResolverResults, _iterator, _step, option, _iterator2, _step2, _option, _iterator3, _step3, _option2, elapsedTimeMilli, transformedOptions, displayedUsers, is5xxEvent, onErrorProducedError, defaultUsers, _elapsedTimeMilli;
114
114
  return _regeneratorRuntime.wrap(function _callee$(_context) {
115
115
  while (1) switch (_context.prev = _context.next) {
116
116
  case 0:
117
117
  _this$state = _this.state, query = _this$state.query, sessionId = _this$state.sessionId, closed = _this$state.closed;
118
- _this$props = _this.props, baseUrl = _this$props.baseUrl, childObjectId = _this$props.childObjectId, containerId = _this$props.containerId, fieldId = _this$props.fieldId, includeGroups = _this$props.includeGroups, includeTeams = _this$props.includeTeams, includeTeamsUpdates = _this$props.includeTeamsUpdates, includeUsers = _this$props.includeUsers, includeNonLicensedUsers = _this$props.includeNonLicensedUsers, intl = _this$props.intl, fetchOptions = _this$props.fetchOptions, maxOptions = _this$props.maxOptions, objectId = _this$props.objectId, onEmpty = _this$props.onEmpty, onError = _this$props.onError, overrideByline = _this$props.overrideByline, displayEmailInByline = _this$props.displayEmailInByline, verifiedTeams = _this$props.verifiedTeams, orgId = _this$props.orgId, principalId = _this$props.principalId, productAttributes = _this$props.productAttributes, productKey = _this$props.productKey, searchQueryFilter = _this$props.searchQueryFilter, siteId = _this$props.siteId, transformOptions = _this$props.transformOptions, userResolvers = _this$props.userResolvers, enableEmailSearch = _this$props.enableEmailSearch;
118
+ _this$props = _this.props, baseUrl = _this$props.baseUrl, childObjectId = _this$props.childObjectId, containerId = _this$props.containerId, fieldId = _this$props.fieldId, includeGroups = _this$props.includeGroups, includeTeams = _this$props.includeTeams, includeTeamsUpdates = _this$props.includeTeamsUpdates, includeUsers = _this$props.includeUsers, includeNonLicensedUsers = _this$props.includeNonLicensedUsers, intl = _this$props.intl, fetchOptions = _this$props.fetchOptions, maxOptions = _this$props.maxOptions, objectId = _this$props.objectId, onEmpty = _this$props.onEmpty, onError = _this$props.onError, overrideByline = _this$props.overrideByline, displayEmailInByline = _this$props.displayEmailInByline, verifiedTeams = _this$props.verifiedTeams, orgId = _this$props.orgId, principalId = _this$props.principalId, productAttributes = _this$props.productAttributes, productKey = _this$props.productKey, restrictTo = _this$props.restrictTo, searchQueryFilter = _this$props.searchQueryFilter, siteId = _this$props.siteId, transformOptions = _this$props.transformOptions, userResolvers = _this$props.userResolvers, enableEmailSearch = _this$props.enableEmailSearch;
119
119
  maxNumberOfResults = maxOptions || 100;
120
120
  startTime = window.performance.now(); // Check if this is an email search
121
121
  isEmail = enableEmailSearch && isEmailQuery(query);
122
- recommendationsRequest = {
122
+ recommendationsRequest = _objectSpread({
123
123
  baseUrl: baseUrl,
124
124
  context: {
125
125
  containerId: containerId,
@@ -141,7 +141,8 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
141
141
  maxNumberOfResults: maxNumberOfResults,
142
142
  query: query,
143
143
  searchEmail: isEmail,
144
- verifiedTeams: verifiedTeams,
144
+ // Only request verified teams when both the prop is true and the feature flag is enabled
145
+ verifiedTeams: verifiedTeams && fg('smart-user-picker-managed-teams-gate'),
145
146
  /*
146
147
  For email-based searches, we have decided to filter out apps.
147
148
  Also - because the other 2 filters ((NOT not_mentionable:true) AND (account_status:active)) are included
@@ -149,7 +150,9 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
149
150
  Further ref: https://developer.atlassian.com/platform/user-recommendations/guides/frequently-asked-questions/#filter-behavior
150
151
  */
151
152
  searchQueryFilter: isEmail && !searchQueryFilter ? '(NOT not_mentionable:true) AND (account_status:active) AND (NOT account_type:app)' : searchQueryFilter
152
- };
153
+ }, restrictTo && fg('smart-user-picker-restrict-to-gate') && {
154
+ restrictTo: restrictTo
155
+ });
153
156
  _context.prev = 6;
154
157
  _query = _this.state.query;
155
158
  _this.fireEvent(requestUsersEvent);
@@ -245,19 +248,6 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
245
248
  }
246
249
  }
247
250
 
248
- // Filter to only verified teams when verifiedTeams is true and feature flag is enabled
249
- if (verifiedTeams && includeTeams && fg('smart-user-picker-managed-teams-gate')) {
250
- recommendedUsers = recommendedUsers.filter(function (option) {
251
- if (isTeam(option)) {
252
- // Only include teams that are verified
253
- // The verified property is set by the transformer from the server response
254
- var team = option;
255
- return team.verified === true;
256
- }
257
- return true; // Keep non-team options
258
- });
259
- }
260
-
261
251
  // Track if email search found matches for conditional allowEmail logic
262
252
  if (isEmail) {
263
253
  _this.lastEmailSearchFoundMatches = recommendedUsers.length > 0;
@@ -266,50 +256,50 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
266
256
  }
267
257
  elapsedTimeMilli = window.performance.now() - startTime;
268
258
  if (!transformOptions) {
269
- _context.next = 42;
259
+ _context.next = 41;
270
260
  break;
271
261
  }
272
- _context.next = 39;
262
+ _context.next = 38;
273
263
  return transformOptions(recommendedUsers, _query);
274
- case 39:
264
+ case 38:
275
265
  _context.t0 = _context.sent;
276
- _context.next = 43;
266
+ _context.next = 42;
277
267
  break;
278
- case 42:
268
+ case 41:
279
269
  _context.t0 = recommendedUsers;
280
- case 43:
270
+ case 42:
281
271
  transformedOptions = _context.t0;
282
272
  if (!(transformedOptions.length === 0 && onEmpty)) {
283
- _context.next = 59;
273
+ _context.next = 58;
284
274
  break;
285
275
  }
286
- _context.next = 47;
276
+ _context.next = 46;
287
277
  return onEmpty(_query);
288
- case 47:
278
+ case 46:
289
279
  _context.t3 = _yield$onEmpty = _context.sent;
290
280
  _context.t2 = _context.t3 !== null;
291
281
  if (!_context.t2) {
292
- _context.next = 51;
282
+ _context.next = 50;
293
283
  break;
294
284
  }
295
285
  _context.t2 = _yield$onEmpty !== void 0;
296
- case 51:
286
+ case 50:
297
287
  if (!_context.t2) {
298
- _context.next = 55;
288
+ _context.next = 54;
299
289
  break;
300
290
  }
301
291
  _context.t4 = _yield$onEmpty;
302
- _context.next = 56;
292
+ _context.next = 55;
303
293
  break;
304
- case 55:
294
+ case 54:
305
295
  _context.t4 = [];
306
- case 56:
296
+ case 55:
307
297
  _context.t1 = _context.t4;
308
- _context.next = 60;
298
+ _context.next = 59;
309
299
  break;
310
- case 59:
300
+ case 58:
311
301
  _context.t1 = transformedOptions;
312
- case 60:
302
+ case 59:
313
303
  displayedUsers = _context.t1;
314
304
  _this.setState(function (state) {
315
305
  var applicable = state.query === _query;
@@ -331,10 +321,10 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
331
321
  loading: loading
332
322
  };
333
323
  });
334
- _context.next = 93;
324
+ _context.next = 92;
335
325
  break;
336
- case 64:
337
- _context.prev = 64;
326
+ case 63:
327
+ _context.prev = 63;
338
328
  _context.t5 = _context["catch"](6);
339
329
  is5xxEvent = checkIf500Event(_context.t5.statusCode);
340
330
  if (!closed && !onError && is5xxEvent) {
@@ -347,35 +337,35 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
347
337
  });
348
338
  onErrorProducedError = false;
349
339
  defaultUsers = [];
350
- _context.prev = 71;
340
+ _context.prev = 70;
351
341
  if (!onError) {
352
- _context.next = 81;
342
+ _context.next = 80;
353
343
  break;
354
344
  }
355
- _context.next = 75;
345
+ _context.next = 74;
356
346
  return onError(_context.t5, recommendationsRequest);
357
- case 75:
347
+ case 74:
358
348
  _context.t7 = _context.sent;
359
349
  if (_context.t7) {
360
- _context.next = 78;
350
+ _context.next = 77;
361
351
  break;
362
352
  }
363
353
  _context.t7 = [];
364
- case 78:
354
+ case 77:
365
355
  _context.t6 = _context.t7;
366
- _context.next = 82;
356
+ _context.next = 81;
367
357
  break;
368
- case 81:
358
+ case 80:
369
359
  _context.t6 = [];
370
- case 82:
360
+ case 81:
371
361
  defaultUsers = _context.t6;
372
- _context.next = 88;
362
+ _context.next = 87;
373
363
  break;
374
- case 85:
375
- _context.prev = 85;
376
- _context.t8 = _context["catch"](71);
364
+ case 84:
365
+ _context.prev = 84;
366
+ _context.t8 = _context["catch"](70);
377
367
  onErrorProducedError = true;
378
- case 88:
368
+ case 87:
379
369
  if (onErrorProducedError && is5xxEvent) {
380
370
  // Log error from fallback data source `onError` to UFO
381
371
  _this.optionsShownUfoExperienceInstance.failure(ufoEndStateConfig(_this.props.fieldId));
@@ -392,11 +382,11 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
392
382
  elapsedTimeMilli: _elapsedTimeMilli,
393
383
  productAttributes: productAttributes
394
384
  });
395
- case 93:
385
+ case 92:
396
386
  case "end":
397
387
  return _context.stop();
398
388
  }
399
- }, _callee, null, [[6, 64], [71, 85]]);
389
+ }, _callee, null, [[6, 63], [70, 84]]);
400
390
  })), (_this$props$debounceT = _this.props.debounceTime) !== null && _this$props$debounceT !== void 0 ? _this$props$debounceT : 0));
401
391
  _defineProperty(_this, "onInputChange", function (newQuery, sessionId) {
402
392
  var query = newQuery || '';
@@ -20,6 +20,7 @@ var getUserRecommendations = function getUserRecommendations(request, intl) {
20
20
  includeNonLicensedUsers: request.includeNonLicensedUsers,
21
21
  maxNumberOfResults: request.maxNumberOfResults,
22
22
  performSearchQueryOnly: false,
23
+ isVerifiedTeamFilter: request.verifiedTeams,
23
24
  searchQuery: _objectSpread(_objectSpread({
24
25
  cpusQueryHighlights: {
25
26
  query: '',
@@ -33,7 +34,7 @@ var getUserRecommendations = function getUserRecommendations(request, intl) {
33
34
  filter: request.searchQueryFilter || '',
34
35
  minimumAccessLevel: 'APPLICATION',
35
36
  queryString: request.query,
36
- restrictTo: {
37
+ restrictTo: request.restrictTo || {
37
38
  userIds: [],
38
39
  groupIds: []
39
40
  },
@@ -12,6 +12,10 @@ export interface Context {
12
12
  organizationId?: string;
13
13
  productAttributes?: ProductAttributes;
14
14
  }
15
+ export interface RestrictionFilter {
16
+ userIds?: string[];
17
+ groupIds?: string[];
18
+ }
15
19
  export interface RecommendationRequest {
16
20
  baseUrl?: string;
17
21
  context: Context;
@@ -24,6 +28,7 @@ export interface RecommendationRequest {
24
28
  includeNonLicensedUsers?: boolean;
25
29
  searchEmail?: boolean;
26
30
  verifiedTeams?: boolean;
31
+ restrictTo?: RestrictionFilter;
27
32
  }
28
33
  type OnError = (error: any, request: RecommendationRequest) => Promise<OptionData[]> | void;
29
34
  type OnValueError = (error: any, defaultValue: DefaultValue) => Promise<OptionData[]> | void;
@@ -247,6 +252,12 @@ export interface SmartProps {
247
252
  isFooterFocused?: boolean;
248
253
  /** Sets if the footer is focused or not. This is needed to keep the menu open when the footer is focused */
249
254
  setIsFooterFocused?: React.Dispatch<React.SetStateAction<boolean>>;
255
+ /**
256
+ * Restricts the recommendations to specific users and/or groups.
257
+ * If provided, only users matching the provided user IDs and/or groups matching the provided group IDs will be included in the results.
258
+ * @example { userIds: ["123", "456"], groupIds: ["789"] }
259
+ */
260
+ restrictTo?: RestrictionFilter;
250
261
  }
251
262
  export interface Props extends SmartProps, UserPickerProps, WithAnalyticsEventsProps {
252
263
  /**
@@ -12,6 +12,10 @@ export interface Context {
12
12
  organizationId?: string;
13
13
  productAttributes?: ProductAttributes;
14
14
  }
15
+ export interface RestrictionFilter {
16
+ userIds?: string[];
17
+ groupIds?: string[];
18
+ }
15
19
  export interface RecommendationRequest {
16
20
  baseUrl?: string;
17
21
  context: Context;
@@ -24,6 +28,7 @@ export interface RecommendationRequest {
24
28
  includeNonLicensedUsers?: boolean;
25
29
  searchEmail?: boolean;
26
30
  verifiedTeams?: boolean;
31
+ restrictTo?: RestrictionFilter;
27
32
  }
28
33
  type OnError = (error: any, request: RecommendationRequest) => Promise<OptionData[]> | void;
29
34
  type OnValueError = (error: any, defaultValue: DefaultValue) => Promise<OptionData[]> | void;
@@ -247,6 +252,12 @@ export interface SmartProps {
247
252
  isFooterFocused?: boolean;
248
253
  /** Sets if the footer is focused or not. This is needed to keep the menu open when the footer is focused */
249
254
  setIsFooterFocused?: React.Dispatch<React.SetStateAction<boolean>>;
255
+ /**
256
+ * Restricts the recommendations to specific users and/or groups.
257
+ * If provided, only users matching the provided user IDs and/or groups matching the provided group IDs will be included in the results.
258
+ * @example { userIds: ["123", "456"], groupIds: ["789"] }
259
+ */
260
+ restrictTo?: RestrictionFilter;
250
261
  }
251
262
  export interface Props extends SmartProps, UserPickerProps, WithAnalyticsEventsProps {
252
263
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/smart-user-picker",
3
- "version": "8.7.0",
3
+ "version": "8.9.0",
4
4
  "license": "Apache-2.0",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -38,7 +38,7 @@
38
38
  "@atlaskit/analytics-next": "^11.1.0",
39
39
  "@atlaskit/platform-feature-flags": "^1.1.0",
40
40
  "@atlaskit/ufo": "^0.4.0",
41
- "@atlaskit/user-picker": "^11.18.0",
41
+ "@atlaskit/user-picker": "^11.19.0",
42
42
  "@babel/runtime": "^7.0.0",
43
43
  "lodash": "^4.17.21",
44
44
  "memoize-one": "^6.0.0",
@@ -51,9 +51,9 @@
51
51
  },
52
52
  "devDependencies": {
53
53
  "@atlaskit/select": "^21.6.0",
54
- "@atlaskit/util-data-test": "^18.3.0",
54
+ "@atlaskit/util-data-test": "^18.4.0",
55
55
  "@testing-library/dom": "^10.1.0",
56
- "@testing-library/react": "^13.4.0",
56
+ "@testing-library/react": "^16.3.0",
57
57
  "@testing-library/user-event": "^14.4.3",
58
58
  "enzyme": "^3.10.0",
59
59
  "fetch-mock": "^8.0.0",
@@ -69,6 +69,9 @@
69
69
  },
70
70
  "smart-user-picker-managed-teams-gate": {
71
71
  "type": "boolean"
72
+ },
73
+ "smart-user-picker-restrict-to-gate": {
74
+ "type": "boolean"
72
75
  }
73
76
  }
74
77
  }