@atlaskit/smart-user-picker 8.9.2 → 9.0.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,20 @@
1
1
  # @atlassian/smart-user-picker
2
2
 
3
+ ## 9.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [`fe65ecbc97c01`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/fe65ecbc97c01) -
8
+ Filter team results to directory-synced teams only, so only supported teams can be selected and
9
+ unsynced teams do not cause errors.
10
+
11
+ ## 8.9.3
12
+
13
+ ### Patch Changes
14
+
15
+ - [`fdfc2acd47acd`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/fdfc2acd47acd) -
16
+ Add @atlassian/a11y-jest-testing to devDependencies.
17
+
3
18
  ## 8.9.2
4
19
 
5
20
  ### Patch Changes
@@ -1,5 +1,5 @@
1
1
  {
2
- "extends": "../../../../tsconfig.entry-points.jira.json",
2
+ "extends": "../../../../tsconfig.local-consumption.json",
3
3
  "compilerOptions": {
4
4
  "target": "es5",
5
5
  "outDir": "../../../../../jira/tsDist/@atlaskit__smart-user-picker/app",
@@ -1,5 +1,5 @@
1
1
  {
2
- "extends": "../../../../tsconfig.entry-points.products.json",
2
+ "extends": "../../../../tsconfig.local-consumption.json",
3
3
  "compilerOptions": {
4
4
  "target": "es5",
5
5
  "outDir": "../../../../../tsDist/@atlaskit__smart-user-picker/app",
@@ -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, 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;
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, isTeamSyncedToGroupDirectoryFilter, 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, userMatches, 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, restrictTo = _this$props.restrictTo, 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, isTeamSyncedToGroupDirectoryFilter = _this$props.isTeamSyncedToGroupDirectoryFilter, 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 = _objectSpread(_objectSpread({
130
+ recommendationsRequest = _objectSpread(_objectSpread(_objectSpread({
131
131
  baseUrl: baseUrl,
132
132
  context: {
133
133
  containerId: containerId,
@@ -161,6 +161,8 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
161
161
  searchQueryFilter: isEmail && !searchQueryFilter ? '(NOT not_mentionable:true) AND (account_status:active) AND (NOT account_type:app)' : searchQueryFilter
162
162
  }, restrictTo && (0, _platformFeatureFlags.fg)('smart-user-picker-restrict-to-gate') && {
163
163
  restrictTo: restrictTo
164
+ }), isTeamSyncedToGroupDirectoryFilter === true && {
165
+ isTeamSyncedToGroupDirectoryFilter: true
164
166
  });
165
167
  _context.prev = 6;
166
168
  _query = _this.state.query;
@@ -259,7 +261,15 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
259
261
 
260
262
  // Track if email search found matches for conditional allowEmail logic
261
263
  if (isEmail) {
262
- _this.lastEmailSearchFoundMatches = recommendedUsers.length > 0;
264
+ if ((0, _platformFeatureFlags.fg)('smart_user_picker_allow_email_if_team_is_found')) {
265
+ // Only count user/external user matches, not teams or groups
266
+ userMatches = recommendedUsers.filter(function (user) {
267
+ return (0, _userPicker.isUser)(user) || (0, _userPicker.isExternalUser)(user);
268
+ });
269
+ _this.lastEmailSearchFoundMatches = userMatches.length > 0;
270
+ } else {
271
+ _this.lastEmailSearchFoundMatches = recommendedUsers.length > 0;
272
+ }
263
273
  } else {
264
274
  _this.lastEmailSearchFoundMatches = false;
265
275
  }
@@ -572,9 +582,16 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
572
582
  // Determine whether to allow email selection based on allowEmailSelectionWhenEmailMatched, if needed
573
583
  var shouldAllowEmail = allowEmail;
574
584
  if (allowEmail && enableEmailSearch && !allowEmailSelectionWhenEmailMatched) {
575
- // Only allow email selection if we're in an email search that found no matches
576
585
  var isCurrentQueryEmail = isEmailQuery(this.state.query);
577
- shouldAllowEmail = !isCurrentQueryEmail || !this.lastEmailSearchFoundMatches;
586
+ if ((0, _platformFeatureFlags.fg)('smart_user_picker_allow_email_if_team_is_found')) {
587
+ // Only allow email selection when:
588
+ // 1. The query matches email format (validated by regex)
589
+ // 2. No user/external user matches were found (only teams/groups suggested)
590
+ shouldAllowEmail = isCurrentQueryEmail && !this.lastEmailSearchFoundMatches;
591
+ } else {
592
+ // Only allow email selection if we're in an email search that found no matches
593
+ shouldAllowEmail = !isCurrentQueryEmail || !this.lastEmailSearchFoundMatches;
594
+ }
578
595
  }
579
596
  return /*#__PURE__*/_react.default.createElement(_MessagesIntlProvider.default, null, /*#__PURE__*/_react.default.createElement(_userPicker.default, (0, _extends2.default)({}, restProps, {
580
597
  allowEmail: shouldAllowEmail,
@@ -27,8 +27,10 @@ var getUserRecommendations = function getUserRecommendations(request, intl) {
27
27
  includeNonLicensedUsers: request.includeNonLicensedUsers,
28
28
  maxNumberOfResults: request.maxNumberOfResults,
29
29
  performSearchQueryOnly: false,
30
- searchQuery: _objectSpread(_objectSpread(_objectSpread({}, request.verifiedTeams === true && {
30
+ searchQuery: _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, request.verifiedTeams === true && {
31
31
  isVerifiedTeamFilter: true
32
+ }), request.isTeamSyncedToGroupDirectoryFilter === true && {
33
+ isTeamSyncedToGroupDirectoryFilter: true
32
34
  }), {}, {
33
35
  cpusQueryHighlights: {
34
36
  query: '',
@@ -42,6 +42,7 @@ var transformUser = function transformUser(item, intl) {
42
42
  };
43
43
  }
44
44
  if (type === _types.EntityType.TEAM) {
45
+ var _team$type;
45
46
  var team = item;
46
47
  return {
47
48
  id: team.id,
@@ -53,7 +54,8 @@ var transformUser = function transformUser(item, intl) {
53
54
  includesYou: team.includesYou,
54
55
  avatarUrl: team.largeAvatarImageUrl || team.smallAvatarImageUrl,
55
56
  tooltip: team.displayName,
56
- verified: team.verified
57
+ verified: team.verified,
58
+ teamTypeName: (_team$type = team.type) === null || _team$type === void 0 ? void 0 : _team$type.name
57
59
  };
58
60
  }
59
61
  if (type === _types.EntityType.GROUP) {
@@ -111,6 +111,7 @@ export class SmartUserPickerWithoutAnalytics extends React.Component {
111
111
  restrictTo,
112
112
  searchQueryFilter,
113
113
  siteId,
114
+ isTeamSyncedToGroupDirectoryFilter,
114
115
  transformOptions,
115
116
  userResolvers,
116
117
  enableEmailSearch
@@ -154,6 +155,9 @@ export class SmartUserPickerWithoutAnalytics extends React.Component {
154
155
  searchQueryFilter: isEmail && !searchQueryFilter ? '(NOT not_mentionable:true) AND (account_status:active) AND (NOT account_type:app)' : searchQueryFilter,
155
156
  ...(restrictTo && fg('smart-user-picker-restrict-to-gate') && {
156
157
  restrictTo
158
+ }),
159
+ ...(isTeamSyncedToGroupDirectoryFilter === true && {
160
+ isTeamSyncedToGroupDirectoryFilter: true
157
161
  })
158
162
  };
159
163
  try {
@@ -208,7 +212,13 @@ export class SmartUserPickerWithoutAnalytics extends React.Component {
208
212
 
209
213
  // Track if email search found matches for conditional allowEmail logic
210
214
  if (isEmail) {
211
- this.lastEmailSearchFoundMatches = recommendedUsers.length > 0;
215
+ if (fg('smart_user_picker_allow_email_if_team_is_found')) {
216
+ // Only count user/external user matches, not teams or groups
217
+ const userMatches = recommendedUsers.filter(user => isUser(user) || isExternalUser(user));
218
+ this.lastEmailSearchFoundMatches = userMatches.length > 0;
219
+ } else {
220
+ this.lastEmailSearchFoundMatches = recommendedUsers.length > 0;
221
+ }
212
222
  } else {
213
223
  this.lastEmailSearchFoundMatches = false;
214
224
  }
@@ -416,9 +426,16 @@ export class SmartUserPickerWithoutAnalytics extends React.Component {
416
426
  // Determine whether to allow email selection based on allowEmailSelectionWhenEmailMatched, if needed
417
427
  let shouldAllowEmail = allowEmail;
418
428
  if (allowEmail && enableEmailSearch && !allowEmailSelectionWhenEmailMatched) {
419
- // Only allow email selection if we're in an email search that found no matches
420
429
  const isCurrentQueryEmail = isEmailQuery(this.state.query);
421
- shouldAllowEmail = !isCurrentQueryEmail || !this.lastEmailSearchFoundMatches;
430
+ if (fg('smart_user_picker_allow_email_if_team_is_found')) {
431
+ // Only allow email selection when:
432
+ // 1. The query matches email format (validated by regex)
433
+ // 2. No user/external user matches were found (only teams/groups suggested)
434
+ shouldAllowEmail = isCurrentQueryEmail && !this.lastEmailSearchFoundMatches;
435
+ } else {
436
+ // Only allow email selection if we're in an email search that found no matches
437
+ shouldAllowEmail = !isCurrentQueryEmail || !this.lastEmailSearchFoundMatches;
438
+ }
422
439
  }
423
440
  return /*#__PURE__*/React.createElement(MessagesIntlProvider, null, /*#__PURE__*/React.createElement(UserPicker, _extends({}, restProps, {
424
441
  allowEmail: shouldAllowEmail,
@@ -21,6 +21,9 @@ const getUserRecommendations = (request, intl) => {
21
21
  ...(request.verifiedTeams === true && {
22
22
  isVerifiedTeamFilter: true
23
23
  }),
24
+ ...(request.isTeamSyncedToGroupDirectoryFilter === true && {
25
+ isTeamSyncedToGroupDirectoryFilter: true
26
+ }),
24
27
  cpusQueryHighlights: {
25
28
  query: '',
26
29
  field: ''
@@ -36,6 +36,7 @@ const transformUser = (item, intl) => {
36
36
  };
37
37
  }
38
38
  if (type === EntityType.TEAM) {
39
+ var _team$type;
39
40
  const team = item;
40
41
  return {
41
42
  id: team.id,
@@ -47,7 +48,8 @@ const transformUser = (item, intl) => {
47
48
  includesYou: team.includesYou,
48
49
  avatarUrl: team.largeAvatarImageUrl || team.smallAvatarImageUrl,
49
50
  tooltip: team.displayName,
50
- verified: team.verified
51
+ verified: team.verified,
52
+ teamTypeName: (_team$type = team.type) === null || _team$type === void 0 ? void 0 : _team$type.name
51
53
  };
52
54
  }
53
55
  if (type === EntityType.GROUP) {
@@ -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, 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;
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, isTeamSyncedToGroupDirectoryFilter, 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, userMatches, 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, restrictTo = _this$props.restrictTo, 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, isTeamSyncedToGroupDirectoryFilter = _this$props.isTeamSyncedToGroupDirectoryFilter, 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 = _objectSpread(_objectSpread({
122
+ recommendationsRequest = _objectSpread(_objectSpread(_objectSpread({
123
123
  baseUrl: baseUrl,
124
124
  context: {
125
125
  containerId: containerId,
@@ -153,6 +153,8 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
153
153
  searchQueryFilter: isEmail && !searchQueryFilter ? '(NOT not_mentionable:true) AND (account_status:active) AND (NOT account_type:app)' : searchQueryFilter
154
154
  }, restrictTo && fg('smart-user-picker-restrict-to-gate') && {
155
155
  restrictTo: restrictTo
156
+ }), isTeamSyncedToGroupDirectoryFilter === true && {
157
+ isTeamSyncedToGroupDirectoryFilter: true
156
158
  });
157
159
  _context.prev = 6;
158
160
  _query = _this.state.query;
@@ -251,7 +253,15 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
251
253
 
252
254
  // Track if email search found matches for conditional allowEmail logic
253
255
  if (isEmail) {
254
- _this.lastEmailSearchFoundMatches = recommendedUsers.length > 0;
256
+ if (fg('smart_user_picker_allow_email_if_team_is_found')) {
257
+ // Only count user/external user matches, not teams or groups
258
+ userMatches = recommendedUsers.filter(function (user) {
259
+ return isUser(user) || isExternalUser(user);
260
+ });
261
+ _this.lastEmailSearchFoundMatches = userMatches.length > 0;
262
+ } else {
263
+ _this.lastEmailSearchFoundMatches = recommendedUsers.length > 0;
264
+ }
255
265
  } else {
256
266
  _this.lastEmailSearchFoundMatches = false;
257
267
  }
@@ -564,9 +574,16 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
564
574
  // Determine whether to allow email selection based on allowEmailSelectionWhenEmailMatched, if needed
565
575
  var shouldAllowEmail = allowEmail;
566
576
  if (allowEmail && enableEmailSearch && !allowEmailSelectionWhenEmailMatched) {
567
- // Only allow email selection if we're in an email search that found no matches
568
577
  var isCurrentQueryEmail = isEmailQuery(this.state.query);
569
- shouldAllowEmail = !isCurrentQueryEmail || !this.lastEmailSearchFoundMatches;
578
+ if (fg('smart_user_picker_allow_email_if_team_is_found')) {
579
+ // Only allow email selection when:
580
+ // 1. The query matches email format (validated by regex)
581
+ // 2. No user/external user matches were found (only teams/groups suggested)
582
+ shouldAllowEmail = isCurrentQueryEmail && !this.lastEmailSearchFoundMatches;
583
+ } else {
584
+ // Only allow email selection if we're in an email search that found no matches
585
+ shouldAllowEmail = !isCurrentQueryEmail || !this.lastEmailSearchFoundMatches;
586
+ }
570
587
  }
571
588
  return /*#__PURE__*/React.createElement(MessagesIntlProvider, null, /*#__PURE__*/React.createElement(UserPicker, _extends({}, restProps, {
572
589
  allowEmail: shouldAllowEmail,
@@ -20,8 +20,10 @@ var getUserRecommendations = function getUserRecommendations(request, intl) {
20
20
  includeNonLicensedUsers: request.includeNonLicensedUsers,
21
21
  maxNumberOfResults: request.maxNumberOfResults,
22
22
  performSearchQueryOnly: false,
23
- searchQuery: _objectSpread(_objectSpread(_objectSpread({}, request.verifiedTeams === true && {
23
+ searchQuery: _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, request.verifiedTeams === true && {
24
24
  isVerifiedTeamFilter: true
25
+ }), request.isTeamSyncedToGroupDirectoryFilter === true && {
26
+ isTeamSyncedToGroupDirectoryFilter: true
25
27
  }), {}, {
26
28
  cpusQueryHighlights: {
27
29
  query: '',
@@ -36,6 +36,7 @@ var transformUser = function transformUser(item, intl) {
36
36
  };
37
37
  }
38
38
  if (type === EntityType.TEAM) {
39
+ var _team$type;
39
40
  var team = item;
40
41
  return {
41
42
  id: team.id,
@@ -47,7 +48,8 @@ var transformUser = function transformUser(item, intl) {
47
48
  includesYou: team.includesYou,
48
49
  avatarUrl: team.largeAvatarImageUrl || team.smallAvatarImageUrl,
49
50
  tooltip: team.displayName,
50
- verified: team.verified
51
+ verified: team.verified,
52
+ teamTypeName: (_team$type = team.type) === null || _team$type === void 0 ? void 0 : _team$type.name
51
53
  };
52
54
  }
53
55
  if (type === EntityType.GROUP) {
@@ -29,6 +29,12 @@ export interface RecommendationRequest {
29
29
  searchEmail?: boolean;
30
30
  verifiedTeams?: boolean;
31
31
  restrictTo?: RestrictionFilter;
32
+ /**
33
+ * When true, URS returns only userbase-aligned teams (teams synced to Identity).
34
+ * Confluence uses this for teams-as-principals to avoid errors when users select teams
35
+ * that are not yet mirrored to Identity (e.g. old org-scoped teams in NonVortex orgs).
36
+ */
37
+ isTeamSyncedToGroupDirectoryFilter?: boolean;
32
38
  }
33
39
  type OnError = (error: any, request: RecommendationRequest) => Promise<OptionData[]> | void;
34
40
  type OnValueError = (error: any, defaultValue: DefaultValue) => Promise<OptionData[]> | void;
@@ -211,7 +217,9 @@ export interface SmartProps {
211
217
  /**
212
218
  * When both allowEmail and enableEmailSearch are true, this controls whether both email entry
213
219
  * and matched user entries can be selected simultaneously.
214
- * If false, only allows email selection when no users are found.
220
+ * If false, only allows email selection when:
221
+ * 1. The query matches email format (validated by regex)
222
+ * 2. No user/external user matches are found (teams/groups don't suppress email entry)
215
223
  * @default true
216
224
  */
217
225
  allowEmailSelectionWhenEmailMatched?: boolean;
@@ -258,6 +266,12 @@ export interface SmartProps {
258
266
  * @example { userIds: ["123", "456"], groupIds: ["789"] }
259
267
  */
260
268
  restrictTo?: RestrictionFilter;
269
+ /**
270
+ * When true, URS returns only userbase-aligned teams (teams synced to Identity).
271
+ * Confluence uses this for teams-as-principals to avoid errors when users select teams
272
+ * that are not yet mirrored to Identity (e.g. old org-scoped teams in NonVortex orgs).
273
+ */
274
+ isTeamSyncedToGroupDirectoryFilter?: boolean;
261
275
  }
262
276
  export interface Props extends SmartProps, UserPickerProps, WithAnalyticsEventsProps {
263
277
  /**
@@ -29,6 +29,12 @@ export interface RecommendationRequest {
29
29
  searchEmail?: boolean;
30
30
  verifiedTeams?: boolean;
31
31
  restrictTo?: RestrictionFilter;
32
+ /**
33
+ * When true, URS returns only userbase-aligned teams (teams synced to Identity).
34
+ * Confluence uses this for teams-as-principals to avoid errors when users select teams
35
+ * that are not yet mirrored to Identity (e.g. old org-scoped teams in NonVortex orgs).
36
+ */
37
+ isTeamSyncedToGroupDirectoryFilter?: boolean;
32
38
  }
33
39
  type OnError = (error: any, request: RecommendationRequest) => Promise<OptionData[]> | void;
34
40
  type OnValueError = (error: any, defaultValue: DefaultValue) => Promise<OptionData[]> | void;
@@ -211,7 +217,9 @@ export interface SmartProps {
211
217
  /**
212
218
  * When both allowEmail and enableEmailSearch are true, this controls whether both email entry
213
219
  * and matched user entries can be selected simultaneously.
214
- * If false, only allows email selection when no users are found.
220
+ * If false, only allows email selection when:
221
+ * 1. The query matches email format (validated by regex)
222
+ * 2. No user/external user matches are found (teams/groups don't suppress email entry)
215
223
  * @default true
216
224
  */
217
225
  allowEmailSelectionWhenEmailMatched?: boolean;
@@ -258,6 +266,12 @@ export interface SmartProps {
258
266
  * @example { userIds: ["123", "456"], groupIds: ["789"] }
259
267
  */
260
268
  restrictTo?: RestrictionFilter;
269
+ /**
270
+ * When true, URS returns only userbase-aligned teams (teams synced to Identity).
271
+ * Confluence uses this for teams-as-principals to avoid errors when users select teams
272
+ * that are not yet mirrored to Identity (e.g. old org-scoped teams in NonVortex orgs).
273
+ */
274
+ isTeamSyncedToGroupDirectoryFilter?: boolean;
261
275
  }
262
276
  export interface Props extends SmartProps, UserPickerProps, WithAnalyticsEventsProps {
263
277
  /**
package/docs/0-intro.tsx CHANGED
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { code, md, Props, Example, AtlassianInternalWarning } from '@atlaskit/docs';
3
3
 
4
- export default md`
4
+ const _default_1: any = md`
5
5
  ${(<AtlassianInternalWarning />)}
6
6
 
7
7
  Smart User Picker extends on [@atlaskit/user-picker](https://statlas.prod.atl-paas.net/atlassian-frontend/master#packages/elements/user-picker) by providing a ML-backed list of suggested users
@@ -44,3 +44,4 @@ export default md`
44
44
  )}
45
45
 
46
46
  `;
47
+ export default _default_1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/smart-user-picker",
3
- "version": "8.9.2",
3
+ "version": "9.0.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.20.0",
41
+ "@atlaskit/user-picker": "^11.22.0",
42
42
  "@babel/runtime": "^7.0.0",
43
43
  "lodash": "^4.17.21",
44
44
  "memoize-one": "^6.0.0",
@@ -50,8 +50,9 @@
50
50
  "react-dom": "^18.2.0"
51
51
  },
52
52
  "devDependencies": {
53
- "@atlaskit/select": "^21.6.0",
54
- "@atlaskit/util-data-test": "^18.4.0",
53
+ "@atlaskit/select": "^21.7.0",
54
+ "@atlaskit/util-data-test": "^18.5.0",
55
+ "@atlassian/a11y-jest-testing": "^0.10.0",
55
56
  "@testing-library/dom": "^10.1.0",
56
57
  "@testing-library/react": "^16.3.0",
57
58
  "@testing-library/user-event": "^14.4.3",
@@ -72,6 +73,9 @@
72
73
  },
73
74
  "smart-user-picker-restrict-to-gate": {
74
75
  "type": "boolean"
76
+ },
77
+ "smart_user_picker_allow_email_if_team_is_found": {
78
+ "type": "boolean"
75
79
  }
76
80
  }
77
81
  }
@@ -1,22 +0,0 @@
1
- {
2
- "extends": "../tsconfig",
3
- "compilerOptions": {
4
- "target": "es5",
5
- "paths": {}
6
- },
7
- "include": [
8
- "../src/**/*.ts",
9
- "../src/**/*.tsx"
10
- ],
11
- "exclude": [
12
- "../src/**/__tests__/*",
13
- "../src/**/*.test.*",
14
- "../src/**/test.*",
15
- "../src/**/examples.*",
16
- "../src/**/examples/*",
17
- "../src/**/examples/**/*",
18
- "../src/**/*.stories.*",
19
- "../src/**/stories/*",
20
- "../src/**/stories/**/*"
21
- ]
22
- }