@atlaskit/smart-user-picker 11.3.1 → 11.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/cjs/analytics.js +1 -1
- package/dist/cjs/components/SmartUserPicker.js +38 -6
- package/dist/es2019/analytics.js +1 -1
- package/dist/es2019/components/SmartUserPicker.js +38 -4
- package/dist/esm/analytics.js +1 -1
- package/dist/esm/components/SmartUserPicker.js +38 -6
- package/dist/types/components/SmartUserPicker.d.ts +1 -0
- package/package.json +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @atlassian/smart-user-picker
|
|
2
2
|
|
|
3
|
+
## 11.3.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`4bdae29c77827`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/4bdae29c77827) -
|
|
8
|
+
Fix suggestEmailsForDomain being suppressed for partial (non-email) input when enableEmailSearch
|
|
9
|
+
and allowEmailSelectionWhenEmailMatched=false are both set. The WAR-5498 FG cleanup (2026-03-04)
|
|
10
|
+
made the isEmailQuery restriction unconditional, breaking the domain-suggestion creatable option
|
|
11
|
+
(e.g. "asdf@atlassian.com"). Behind the new gate smart_user_picker_suggest_emails_for_domain,
|
|
12
|
+
partial queries now allow the domain suggestion to render, while the email option remains
|
|
13
|
+
suppressed when a full email query matches an existing user, or when the synthesized
|
|
14
|
+
"<query>@<domain>" address already belongs to a returned user.
|
|
15
|
+
|
|
3
16
|
## 11.3.1
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/dist/cjs/analytics.js
CHANGED
|
@@ -12,7 +12,7 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
|
|
|
12
12
|
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; } // eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
13
13
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
14
14
|
var packageName = "@atlaskit/smart-user-picker";
|
|
15
|
-
var packageVersion = "11.3.
|
|
15
|
+
var packageVersion = "11.3.1";
|
|
16
16
|
var startSession = exports.startSession = function startSession() {
|
|
17
17
|
return {
|
|
18
18
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
@@ -32,7 +32,7 @@ var _MessagesIntlProvider = _interopRequireDefault(require("./MessagesIntlProvid
|
|
|
32
32
|
var _types = require("../types");
|
|
33
33
|
var _service = require("../service");
|
|
34
34
|
var _ufoExperiences = require("../ufoExperiences");
|
|
35
|
-
var _excluded = ["allowEmail", "enableEmailSearch", "allowEmailSelectionWhenEmailMatched"];
|
|
35
|
+
var _excluded = ["allowEmail", "enableEmailSearch", "allowEmailSelectionWhenEmailMatched", "suggestEmailsForDomain"];
|
|
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 _t1 in e) "default" !== _t1 && {}.hasOwnProperty.call(e, _t1) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t1)) && (i.get || i.set) ? o(f, _t1, i) : f[_t1] = e[_t1]); return f; })(e, t); }
|
|
37
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; } } }; }
|
|
38
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; } }
|
|
@@ -93,6 +93,9 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
|
|
|
93
93
|
});
|
|
94
94
|
// Track if the last search was an email search that found matches
|
|
95
95
|
(0, _defineProperty2.default)(_this, "lastEmailSearchFoundMatches", false);
|
|
96
|
+
// Track if the domain-suggested email (`<query>@<suggestEmailsForDomain>`) for a partial
|
|
97
|
+
// query already belongs to a returned user, in which case the suggestion is redundant
|
|
98
|
+
(0, _defineProperty2.default)(_this, "lastDomainSuggestionMatchedUser", false);
|
|
96
99
|
(0, _defineProperty2.default)(_this, "abortOptionsShownUfoExperience", function () {
|
|
97
100
|
if (_this.optionsShownUfoExperienceInstance.state.id === _ufo.UFOExperienceState.STARTED.id) {
|
|
98
101
|
// There may be an existing UFO timing running from previous key entry or focus,
|
|
@@ -118,7 +121,7 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
|
|
|
118
121
|
});
|
|
119
122
|
(0, _defineProperty2.default)(_this, "memoizedFilterOptions", (0, _memoizeOne.default)(_this.filterOptions));
|
|
120
123
|
(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, userbaseId, 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, _t, _t2, _t3, _t4, _t5, _t6, _t7, _t8, _t9;
|
|
124
|
+
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, userbaseId, 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, suggestEmailsForDomain, suggestedEmail, elapsedTimeMilli, transformedOptions, displayedUsers, is5xxEvent, onErrorProducedError, defaultUsers, _elapsedTimeMilli, _t, _t2, _t3, _t4, _t5, _t6, _t7, _t8, _t9;
|
|
122
125
|
return _regenerator.default.wrap(function (_context) {
|
|
123
126
|
while (1) switch (_context.prev = _context.next) {
|
|
124
127
|
case 0:
|
|
@@ -259,6 +262,20 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
|
|
|
259
262
|
} else {
|
|
260
263
|
_this.lastEmailSearchFoundMatches = false;
|
|
261
264
|
}
|
|
265
|
+
|
|
266
|
+
// For a partial query with a suggested domain, check whether the email we would
|
|
267
|
+
// synthesize (`<query>@<domain>`) already belongs to one of the returned users. If so,
|
|
268
|
+
// the real user option is shown instead and the email suggestion is suppressed.
|
|
269
|
+
suggestEmailsForDomain = _this.props.suggestEmailsForDomain;
|
|
270
|
+
if (suggestEmailsForDomain && !isEmail && _query) {
|
|
271
|
+
suggestedEmail = "".concat(_query, "@").concat(suggestEmailsForDomain).toLowerCase();
|
|
272
|
+
_this.lastDomainSuggestionMatchedUser = recommendedUsers.some(function (option) {
|
|
273
|
+
var _option$email;
|
|
274
|
+
return ((0, _userPicker.isUser)(option) || (0, _userPicker.isExternalUser)(option)) && ((_option$email = option.email) === null || _option$email === void 0 ? void 0 : _option$email.toLowerCase()) === suggestedEmail;
|
|
275
|
+
});
|
|
276
|
+
} else {
|
|
277
|
+
_this.lastDomainSuggestionMatchedUser = false;
|
|
278
|
+
}
|
|
262
279
|
elapsedTimeMilli = window.performance.now() - startTime;
|
|
263
280
|
if (!transformOptions) {
|
|
264
281
|
_context.next = 7;
|
|
@@ -566,19 +583,34 @@ var SmartUserPickerWithoutAnalytics = exports.SmartUserPickerWithoutAnalytics =
|
|
|
566
583
|
allowEmail = _this$props2.allowEmail,
|
|
567
584
|
enableEmailSearch = _this$props2.enableEmailSearch,
|
|
568
585
|
allowEmailSelectionWhenEmailMatched = _this$props2.allowEmailSelectionWhenEmailMatched,
|
|
586
|
+
suggestEmailsForDomain = _this$props2.suggestEmailsForDomain,
|
|
569
587
|
restProps = (0, _objectWithoutProperties2.default)(_this$props2, _excluded);
|
|
570
588
|
|
|
571
589
|
// Determine whether to allow email selection based on allowEmailSelectionWhenEmailMatched, if needed
|
|
572
590
|
var shouldAllowEmail = allowEmail;
|
|
591
|
+
|
|
592
|
+
// When suggestEmailsForDomain is active, partial (non-email) input must still reach the base
|
|
593
|
+
// picker's domain-suggestion creatable logic, so the isEmailQuery restriction is relaxed for
|
|
594
|
+
// those queries only. Full email queries that matched an existing user remain suppressed.
|
|
595
|
+
var isDomainSuggestionEnabled = (0, _platformFeatureFlags.fg)('smart_user_picker_suggest_emails_for_domain') && !!suggestEmailsForDomain;
|
|
573
596
|
if (allowEmail && enableEmailSearch && !allowEmailSelectionWhenEmailMatched) {
|
|
574
597
|
var isCurrentQueryEmail = isEmailQuery(this.state.query);
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
598
|
+
if (isDomainSuggestionEnabled) {
|
|
599
|
+
// Allow email for partial queries so the "<query>@<domain>" suggestion can render,
|
|
600
|
+
// but still suppress selection when a full email query matched an existing user,
|
|
601
|
+
// or when the synthesized "<query>@<domain>" is itself an existing user's email.
|
|
602
|
+
shouldAllowEmail = isCurrentQueryEmail ? !this.lastEmailSearchFoundMatches // full email: suppress when user matched
|
|
603
|
+
: !this.lastDomainSuggestionMatchedUser; // partial: suppress when synth email matched a user
|
|
604
|
+
} else {
|
|
605
|
+
// Only allow email selection when:
|
|
606
|
+
// 1. The query matches email format (validated by regex)
|
|
607
|
+
// 2. No user/external user matches were found (only teams/groups suggested)
|
|
608
|
+
shouldAllowEmail = isCurrentQueryEmail && !this.lastEmailSearchFoundMatches;
|
|
609
|
+
}
|
|
579
610
|
}
|
|
580
611
|
return /*#__PURE__*/_react.default.createElement(_MessagesIntlProvider.default, null, /*#__PURE__*/_react.default.createElement(_userPicker.default, (0, _extends2.default)({}, restProps, {
|
|
581
612
|
allowEmail: shouldAllowEmail,
|
|
613
|
+
suggestEmailsForDomain: suggestEmailsForDomain,
|
|
582
614
|
onInputChange: this.onInputChange,
|
|
583
615
|
onBlur: this.onBlur,
|
|
584
616
|
onFocus: this.onFocus,
|
package/dist/es2019/analytics.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createAndFireEvent } from '@atlaskit/analytics-next';
|
|
|
3
3
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
4
4
|
import { v4 as uuid } from 'uuid';
|
|
5
5
|
const packageName = "@atlaskit/smart-user-picker";
|
|
6
|
-
const packageVersion = "11.3.
|
|
6
|
+
const packageVersion = "11.3.1";
|
|
7
7
|
export const startSession = () => ({
|
|
8
8
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
9
9
|
id: uuid(),
|
|
@@ -58,6 +58,9 @@ export class SmartUserPickerWithoutAnalytics extends React.Component {
|
|
|
58
58
|
});
|
|
59
59
|
// Track if the last search was an email search that found matches
|
|
60
60
|
_defineProperty(this, "lastEmailSearchFoundMatches", false);
|
|
61
|
+
// Track if the domain-suggested email (`<query>@<suggestEmailsForDomain>`) for a partial
|
|
62
|
+
// query already belongs to a returned user, in which case the suggestion is redundant
|
|
63
|
+
_defineProperty(this, "lastDomainSuggestionMatchedUser", false);
|
|
61
64
|
_defineProperty(this, "abortOptionsShownUfoExperience", () => {
|
|
62
65
|
if (this.optionsShownUfoExperienceInstance.state.id === UFOExperienceState.STARTED.id) {
|
|
63
66
|
// There may be an existing UFO timing running from previous key entry or focus,
|
|
@@ -218,6 +221,22 @@ export class SmartUserPickerWithoutAnalytics extends React.Component {
|
|
|
218
221
|
} else {
|
|
219
222
|
this.lastEmailSearchFoundMatches = false;
|
|
220
223
|
}
|
|
224
|
+
|
|
225
|
+
// For a partial query with a suggested domain, check whether the email we would
|
|
226
|
+
// synthesize (`<query>@<domain>`) already belongs to one of the returned users. If so,
|
|
227
|
+
// the real user option is shown instead and the email suggestion is suppressed.
|
|
228
|
+
const {
|
|
229
|
+
suggestEmailsForDomain
|
|
230
|
+
} = this.props;
|
|
231
|
+
if (suggestEmailsForDomain && !isEmail && query) {
|
|
232
|
+
const suggestedEmail = `${query}@${suggestEmailsForDomain}`.toLowerCase();
|
|
233
|
+
this.lastDomainSuggestionMatchedUser = recommendedUsers.some(option => {
|
|
234
|
+
var _option$email;
|
|
235
|
+
return (isUser(option) || isExternalUser(option)) && ((_option$email = option.email) === null || _option$email === void 0 ? void 0 : _option$email.toLowerCase()) === suggestedEmail;
|
|
236
|
+
});
|
|
237
|
+
} else {
|
|
238
|
+
this.lastDomainSuggestionMatchedUser = false;
|
|
239
|
+
}
|
|
221
240
|
const elapsedTimeMilli = window.performance.now() - startTime;
|
|
222
241
|
const transformedOptions = transformOptions ? await transformOptions(recommendedUsers, query) : recommendedUsers;
|
|
223
242
|
const displayedUsers = transformedOptions.length === 0 && onEmpty ? (_await$onEmpty = await onEmpty(query)) !== null && _await$onEmpty !== void 0 ? _await$onEmpty : [] : transformedOptions;
|
|
@@ -419,20 +438,35 @@ export class SmartUserPickerWithoutAnalytics extends React.Component {
|
|
|
419
438
|
allowEmail,
|
|
420
439
|
enableEmailSearch,
|
|
421
440
|
allowEmailSelectionWhenEmailMatched,
|
|
441
|
+
suggestEmailsForDomain,
|
|
422
442
|
...restProps
|
|
423
443
|
} = this.props;
|
|
424
444
|
|
|
425
445
|
// Determine whether to allow email selection based on allowEmailSelectionWhenEmailMatched, if needed
|
|
426
446
|
let shouldAllowEmail = allowEmail;
|
|
447
|
+
|
|
448
|
+
// When suggestEmailsForDomain is active, partial (non-email) input must still reach the base
|
|
449
|
+
// picker's domain-suggestion creatable logic, so the isEmailQuery restriction is relaxed for
|
|
450
|
+
// those queries only. Full email queries that matched an existing user remain suppressed.
|
|
451
|
+
const isDomainSuggestionEnabled = fg('smart_user_picker_suggest_emails_for_domain') && !!suggestEmailsForDomain;
|
|
427
452
|
if (allowEmail && enableEmailSearch && !allowEmailSelectionWhenEmailMatched) {
|
|
428
453
|
const isCurrentQueryEmail = isEmailQuery(this.state.query);
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
454
|
+
if (isDomainSuggestionEnabled) {
|
|
455
|
+
// Allow email for partial queries so the "<query>@<domain>" suggestion can render,
|
|
456
|
+
// but still suppress selection when a full email query matched an existing user,
|
|
457
|
+
// or when the synthesized "<query>@<domain>" is itself an existing user's email.
|
|
458
|
+
shouldAllowEmail = isCurrentQueryEmail ? !this.lastEmailSearchFoundMatches // full email: suppress when user matched
|
|
459
|
+
: !this.lastDomainSuggestionMatchedUser; // partial: suppress when synth email matched a user
|
|
460
|
+
} else {
|
|
461
|
+
// Only allow email selection when:
|
|
462
|
+
// 1. The query matches email format (validated by regex)
|
|
463
|
+
// 2. No user/external user matches were found (only teams/groups suggested)
|
|
464
|
+
shouldAllowEmail = isCurrentQueryEmail && !this.lastEmailSearchFoundMatches;
|
|
465
|
+
}
|
|
433
466
|
}
|
|
434
467
|
return /*#__PURE__*/React.createElement(MessagesIntlProvider, null, /*#__PURE__*/React.createElement(UserPicker, _extends({}, restProps, {
|
|
435
468
|
allowEmail: shouldAllowEmail,
|
|
469
|
+
suggestEmailsForDomain: suggestEmailsForDomain,
|
|
436
470
|
onInputChange: this.onInputChange,
|
|
437
471
|
onBlur: this.onBlur,
|
|
438
472
|
onFocus: this.onFocus,
|
package/dist/esm/analytics.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createAndFireEvent } from '@atlaskit/analytics-next';
|
|
|
6
6
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
7
7
|
import { v4 as uuid } from 'uuid';
|
|
8
8
|
var packageName = "@atlaskit/smart-user-picker";
|
|
9
|
-
var packageVersion = "11.3.
|
|
9
|
+
var packageVersion = "11.3.1";
|
|
10
10
|
export var startSession = function startSession() {
|
|
11
11
|
return {
|
|
12
12
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
@@ -9,7 +9,7 @@ import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstruct
|
|
|
9
9
|
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
|
10
10
|
import _inherits from "@babel/runtime/helpers/inherits";
|
|
11
11
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
12
|
-
var _excluded = ["allowEmail", "enableEmailSearch", "allowEmailSelectionWhenEmailMatched"];
|
|
12
|
+
var _excluded = ["allowEmail", "enableEmailSearch", "allowEmailSelectionWhenEmailMatched", "suggestEmailsForDomain"];
|
|
13
13
|
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
14
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; } } }; }
|
|
15
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; } }
|
|
@@ -85,6 +85,9 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
|
|
|
85
85
|
});
|
|
86
86
|
// Track if the last search was an email search that found matches
|
|
87
87
|
_defineProperty(_this, "lastEmailSearchFoundMatches", false);
|
|
88
|
+
// Track if the domain-suggested email (`<query>@<suggestEmailsForDomain>`) for a partial
|
|
89
|
+
// query already belongs to a returned user, in which case the suggestion is redundant
|
|
90
|
+
_defineProperty(_this, "lastDomainSuggestionMatchedUser", false);
|
|
88
91
|
_defineProperty(_this, "abortOptionsShownUfoExperience", function () {
|
|
89
92
|
if (_this.optionsShownUfoExperienceInstance.state.id === UFOExperienceState.STARTED.id) {
|
|
90
93
|
// There may be an existing UFO timing running from previous key entry or focus,
|
|
@@ -110,7 +113,7 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
|
|
|
110
113
|
});
|
|
111
114
|
_defineProperty(_this, "memoizedFilterOptions", memoizeOne(_this.filterOptions));
|
|
112
115
|
_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, userbaseId, 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, _t, _t2, _t3, _t4, _t5, _t6, _t7, _t8, _t9;
|
|
116
|
+
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, userbaseId, 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, suggestEmailsForDomain, suggestedEmail, elapsedTimeMilli, transformedOptions, displayedUsers, is5xxEvent, onErrorProducedError, defaultUsers, _elapsedTimeMilli, _t, _t2, _t3, _t4, _t5, _t6, _t7, _t8, _t9;
|
|
114
117
|
return _regeneratorRuntime.wrap(function (_context) {
|
|
115
118
|
while (1) switch (_context.prev = _context.next) {
|
|
116
119
|
case 0:
|
|
@@ -251,6 +254,20 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
|
|
|
251
254
|
} else {
|
|
252
255
|
_this.lastEmailSearchFoundMatches = false;
|
|
253
256
|
}
|
|
257
|
+
|
|
258
|
+
// For a partial query with a suggested domain, check whether the email we would
|
|
259
|
+
// synthesize (`<query>@<domain>`) already belongs to one of the returned users. If so,
|
|
260
|
+
// the real user option is shown instead and the email suggestion is suppressed.
|
|
261
|
+
suggestEmailsForDomain = _this.props.suggestEmailsForDomain;
|
|
262
|
+
if (suggestEmailsForDomain && !isEmail && _query) {
|
|
263
|
+
suggestedEmail = "".concat(_query, "@").concat(suggestEmailsForDomain).toLowerCase();
|
|
264
|
+
_this.lastDomainSuggestionMatchedUser = recommendedUsers.some(function (option) {
|
|
265
|
+
var _option$email;
|
|
266
|
+
return (isUser(option) || isExternalUser(option)) && ((_option$email = option.email) === null || _option$email === void 0 ? void 0 : _option$email.toLowerCase()) === suggestedEmail;
|
|
267
|
+
});
|
|
268
|
+
} else {
|
|
269
|
+
_this.lastDomainSuggestionMatchedUser = false;
|
|
270
|
+
}
|
|
254
271
|
elapsedTimeMilli = window.performance.now() - startTime;
|
|
255
272
|
if (!transformOptions) {
|
|
256
273
|
_context.next = 7;
|
|
@@ -558,19 +575,34 @@ export var SmartUserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Compo
|
|
|
558
575
|
allowEmail = _this$props2.allowEmail,
|
|
559
576
|
enableEmailSearch = _this$props2.enableEmailSearch,
|
|
560
577
|
allowEmailSelectionWhenEmailMatched = _this$props2.allowEmailSelectionWhenEmailMatched,
|
|
578
|
+
suggestEmailsForDomain = _this$props2.suggestEmailsForDomain,
|
|
561
579
|
restProps = _objectWithoutProperties(_this$props2, _excluded);
|
|
562
580
|
|
|
563
581
|
// Determine whether to allow email selection based on allowEmailSelectionWhenEmailMatched, if needed
|
|
564
582
|
var shouldAllowEmail = allowEmail;
|
|
583
|
+
|
|
584
|
+
// When suggestEmailsForDomain is active, partial (non-email) input must still reach the base
|
|
585
|
+
// picker's domain-suggestion creatable logic, so the isEmailQuery restriction is relaxed for
|
|
586
|
+
// those queries only. Full email queries that matched an existing user remain suppressed.
|
|
587
|
+
var isDomainSuggestionEnabled = fg('smart_user_picker_suggest_emails_for_domain') && !!suggestEmailsForDomain;
|
|
565
588
|
if (allowEmail && enableEmailSearch && !allowEmailSelectionWhenEmailMatched) {
|
|
566
589
|
var isCurrentQueryEmail = isEmailQuery(this.state.query);
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
590
|
+
if (isDomainSuggestionEnabled) {
|
|
591
|
+
// Allow email for partial queries so the "<query>@<domain>" suggestion can render,
|
|
592
|
+
// but still suppress selection when a full email query matched an existing user,
|
|
593
|
+
// or when the synthesized "<query>@<domain>" is itself an existing user's email.
|
|
594
|
+
shouldAllowEmail = isCurrentQueryEmail ? !this.lastEmailSearchFoundMatches // full email: suppress when user matched
|
|
595
|
+
: !this.lastDomainSuggestionMatchedUser; // partial: suppress when synth email matched a user
|
|
596
|
+
} else {
|
|
597
|
+
// Only allow email selection when:
|
|
598
|
+
// 1. The query matches email format (validated by regex)
|
|
599
|
+
// 2. No user/external user matches were found (only teams/groups suggested)
|
|
600
|
+
shouldAllowEmail = isCurrentQueryEmail && !this.lastEmailSearchFoundMatches;
|
|
601
|
+
}
|
|
571
602
|
}
|
|
572
603
|
return /*#__PURE__*/React.createElement(MessagesIntlProvider, null, /*#__PURE__*/React.createElement(UserPicker, _extends({}, restProps, {
|
|
573
604
|
allowEmail: shouldAllowEmail,
|
|
605
|
+
suggestEmailsForDomain: suggestEmailsForDomain,
|
|
574
606
|
onInputChange: this.onInputChange,
|
|
575
607
|
onBlur: this.onBlur,
|
|
576
608
|
onFocus: this.onFocus,
|
|
@@ -8,6 +8,7 @@ import type { DebouncedFunc } from 'lodash';
|
|
|
8
8
|
export declare class SmartUserPickerWithoutAnalytics extends React.Component<Props & WrappedComponentProps, State> {
|
|
9
9
|
state: State;
|
|
10
10
|
private lastEmailSearchFoundMatches;
|
|
11
|
+
private lastDomainSuggestionMatchedUser;
|
|
11
12
|
optionsShownUfoExperienceInstance: UFOExperience;
|
|
12
13
|
static defaultProps: {
|
|
13
14
|
baseUrl: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/smart-user-picker",
|
|
3
|
-
"version": "11.3.
|
|
3
|
+
"version": "11.3.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -57,6 +57,9 @@
|
|
|
57
57
|
"react-intl": "^7.0.0"
|
|
58
58
|
},
|
|
59
59
|
"platform-feature-flags": {
|
|
60
|
+
"smart_user_picker_suggest_emails_for_domain": {
|
|
61
|
+
"type": "boolean"
|
|
62
|
+
},
|
|
60
63
|
"smart-user-picker-load-options-gate": {
|
|
61
64
|
"type": "boolean"
|
|
62
65
|
},
|