@atlaskit/user-picker 8.2.1 → 8.3.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,11 @@
1
1
  # @atlaskit/user-picker
2
2
 
3
+ ## 8.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`88551bad1ec`](https://bitbucket.org/atlassian/atlassian-frontend/commits/88551bad1ec) - [ux] Add support for asychronously fetching the sources for an external user when a user hovers over the sources tooltip
8
+
3
9
  ## 8.2.1
4
10
 
5
11
  ### Patch Changes
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ var _typeof = require("@babel/runtime/helpers/typeof");
6
+
7
+ Object.defineProperty(exports, "__esModule", {
8
+ value: true
9
+ });
10
+ exports.useUserSource = exports.ExusUserSourceProvider = void 0;
11
+
12
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
13
+
14
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
15
+
16
+ var _react = _interopRequireWildcard(require("react"));
17
+
18
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
19
+
20
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
21
+
22
+ var ExusUserSourceContext = /*#__PURE__*/(0, _react.createContext)({});
23
+
24
+ var ExusUserSourceProvider = function ExusUserSourceProvider(_ref) {
25
+ var fetchUserSource = _ref.fetchUserSource,
26
+ children = _ref.children;
27
+ return /*#__PURE__*/_react.default.createElement(ExusUserSourceContext.Provider, {
28
+ value: {
29
+ fetchUserSource: fetchUserSource
30
+ }
31
+ }, children);
32
+ };
33
+
34
+ exports.ExusUserSourceProvider = ExusUserSourceProvider;
35
+
36
+ var useUserSource = function useUserSource(accountId, existingSources) {
37
+ var _useContext = (0, _react.useContext)(ExusUserSourceContext),
38
+ fetchUserSource = _useContext.fetchUserSource;
39
+
40
+ var _useState = (0, _react.useState)(new Set(existingSources)),
41
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
42
+ sources = _useState2[0],
43
+ setUserSources = _useState2[1];
44
+
45
+ var _useState3 = (0, _react.useState)(true),
46
+ _useState4 = (0, _slicedToArray2.default)(_useState3, 2),
47
+ loading = _useState4[0],
48
+ setLoading = _useState4[1];
49
+
50
+ var _useState5 = (0, _react.useState)(null),
51
+ _useState6 = (0, _slicedToArray2.default)(_useState5, 2),
52
+ error = _useState6[0],
53
+ setError = _useState6[1];
54
+
55
+ var abortController = (0, _react.useMemo)(function () {
56
+ if (typeof AbortController === 'undefined') {
57
+ return;
58
+ }
59
+
60
+ return new AbortController();
61
+ }, []);
62
+ (0, _react.useEffect)(function () {
63
+ var isMounted = true;
64
+
65
+ var cleanup = function cleanup() {
66
+ abortController === null || abortController === void 0 ? void 0 : abortController.abort();
67
+ isMounted = false;
68
+ };
69
+
70
+ if (!fetchUserSource) {
71
+ setLoading(false);
72
+ return cleanup;
73
+ }
74
+
75
+ if (isMounted) {
76
+ fetchUserSource(accountId, abortController === null || abortController === void 0 ? void 0 : abortController.signal).then(function (externalSources) {
77
+ setLoading(false);
78
+ var externalSourceTypes = externalSources.map(function (source) {
79
+ return source.sourceType;
80
+ });
81
+ setUserSources(new Set([].concat((0, _toConsumableArray2.default)(sources), (0, _toConsumableArray2.default)(externalSourceTypes))));
82
+ }).catch(function (error) {
83
+ setLoading(false);
84
+ setError(error);
85
+ });
86
+ }
87
+
88
+ return cleanup;
89
+ }, [fetchUserSource, accountId, sources, abortController]);
90
+ return {
91
+ sources: Array.from(sources),
92
+ loading: loading,
93
+ error: error
94
+ };
95
+ };
96
+
97
+ exports.useUserSource = useUserSource;
@@ -39,6 +39,8 @@ var _panel = _interopRequireDefault(require("@atlaskit/icon/glyph/editor/panel")
39
39
 
40
40
  var _tooltip = _interopRequireDefault(require("@atlaskit/tooltip"));
41
41
 
42
+ var _spinner = _interopRequireDefault(require("@atlaskit/spinner"));
43
+
42
44
  var _styledComponents = _interopRequireDefault(require("styled-components"));
43
45
 
44
46
  var _slack = require("../assets/slack");
@@ -53,6 +55,8 @@ var _reactIntlNext = require("react-intl-next");
53
55
 
54
56
  var _i18n = require("../i18n");
55
57
 
58
+ var _ExternalUserSourcesContainer = require("../ExternalUserSourcesContainer");
59
+
56
60
  var _templateObject, _templateObject2, _templateObject3, _templateObject4;
57
61
 
58
62
  function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }
@@ -145,12 +149,11 @@ var ExternalUserOption = /*#__PURE__*/function (_React$PureComponent) {
145
149
  });
146
150
  });
147
151
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "getSourcesInfoTooltip", function () {
148
- var _this$props$user$sour;
149
-
150
- return ((_this$props$user$sour = _this.props.user.sources) === null || _this$props$user$sour === void 0 ? void 0 : _this$props$user$sour.length) > 0 ? /*#__PURE__*/_react.default.createElement(_tooltip.default, {
152
+ return _this.props.user.isExternal ? /*#__PURE__*/_react.default.createElement(_tooltip.default, {
151
153
  content: _this.formattedTooltipContent(),
152
154
  position: 'right-start'
153
155
  }, /*#__PURE__*/_react.default.createElement(_panel.default, {
156
+ testId: "source-icon",
154
157
  label: "",
155
158
  size: "large",
156
159
  primaryColor: (0, _tokens.token)('color.text.lowEmphasis', _colors.N200)
@@ -172,18 +175,32 @@ var ExternalUserOption = /*#__PURE__*/function (_React$PureComponent) {
172
175
  }, {
173
176
  key: "formattedTooltipContent",
174
177
  value: function formattedTooltipContent() {
175
- return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("span", null, /*#__PURE__*/_react.default.createElement(_reactIntlNext.FormattedMessage, _i18n.messages.externalUserSourcesHeading)), /*#__PURE__*/_react.default.createElement(SourcesTooltipContainer, null, this.props.user.sources.map(function (s) {
176
- return SourcesInfoMap.get(s);
177
- }).filter(function (s) {
178
- return s;
179
- }).map(function (_ref) {
180
- var key = _ref.key,
181
- icon = _ref.icon,
182
- label = _ref.label;
183
- return /*#__PURE__*/_react.default.createElement(SourceWrapper, {
184
- key: key
185
- }, /*#__PURE__*/_react.default.createElement(ImageContainer, null, icon), /*#__PURE__*/_react.default.createElement("span", null, /*#__PURE__*/_react.default.createElement(_reactIntlNext.FormattedMessage, label)));
186
- })));
178
+ var _this$props$user2 = this.props.user,
179
+ id = _this$props$user2.id,
180
+ sources = _this$props$user2.sources;
181
+ return /*#__PURE__*/_react.default.createElement(_ExternalUserSourcesContainer.ExternalUserSourcesContainer, {
182
+ accountId: id,
183
+ initialSources: sources
184
+ }, function (_ref) {
185
+ var sources = _ref.sources,
186
+ sourcesLoading = _ref.sourcesLoading,
187
+ sourcesError = _ref.sourcesError;
188
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, sourcesError !== null && sources.length === 0 ? /*#__PURE__*/_react.default.createElement("span", null, /*#__PURE__*/_react.default.createElement(_reactIntlNext.FormattedMessage, _i18n.messages.externalUserSourcesError)) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("span", null, /*#__PURE__*/_react.default.createElement(_reactIntlNext.FormattedMessage, _i18n.messages.externalUserSourcesHeading)), /*#__PURE__*/_react.default.createElement(SourcesTooltipContainer, null, sourcesLoading && /*#__PURE__*/_react.default.createElement(_spinner.default, {
189
+ size: "small",
190
+ appearance: "invert"
191
+ }), !sourcesLoading && sources.map(function (s) {
192
+ return SourcesInfoMap.get(s);
193
+ }).filter(function (s) {
194
+ return s;
195
+ }).map(function (_ref2) {
196
+ var key = _ref2.key,
197
+ icon = _ref2.icon,
198
+ label = _ref2.label;
199
+ return /*#__PURE__*/_react.default.createElement(SourceWrapper, {
200
+ key: key
201
+ }, /*#__PURE__*/_react.default.createElement(ImageContainer, null, icon), /*#__PURE__*/_react.default.createElement("span", null, /*#__PURE__*/_react.default.createElement(_reactIntlNext.FormattedMessage, label)));
202
+ }))));
203
+ });
187
204
  }
188
205
  }]);
189
206
  return ExternalUserOption;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.ExternalUserSourcesContainer = void 0;
9
+
10
+ var _react = _interopRequireDefault(require("react"));
11
+
12
+ var _UserSourceProvider = require("../clients/UserSourceProvider");
13
+
14
+ var ExternalUserSourcesContainer = function ExternalUserSourcesContainer(_ref) {
15
+ var children = _ref.children,
16
+ accountId = _ref.accountId,
17
+ _ref$initialSources = _ref.initialSources,
18
+ initialSources = _ref$initialSources === void 0 ? [] : _ref$initialSources;
19
+
20
+ var _useUserSource = (0, _UserSourceProvider.useUserSource)(accountId, initialSources),
21
+ sources = _useUserSource.sources,
22
+ sourcesLoading = _useUserSource.loading,
23
+ sourcesError = _useUserSource.error;
24
+
25
+ if (typeof children === 'function') {
26
+ return children({
27
+ sources: sources,
28
+ sourcesLoading: sourcesLoading,
29
+ sourcesError: sourcesError
30
+ });
31
+ }
32
+
33
+ return _react.default.Children.map(children, function (child) {
34
+ return /*#__PURE__*/_react.default.cloneElement(child, {
35
+ sources: sources,
36
+ sourcesLoading: sourcesLoading,
37
+ sourcesError: sourcesError
38
+ });
39
+ });
40
+ };
41
+
42
+ exports.ExternalUserSourcesContainer = ExternalUserSourcesContainer;
@@ -41,6 +41,8 @@ var _creatableEmailSuggestion = require("./creatableEmailSuggestion");
41
41
 
42
42
  var _MessagesIntlProvider = _interopRequireDefault(require("./MessagesIntlProvider"));
43
43
 
44
+ var _UserSourceProvider = require("./../clients/UserSourceProvider");
45
+
44
46
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
45
47
 
46
48
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
@@ -77,7 +79,8 @@ var UserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Component) {
77
79
  menuPosition = _this$props.menuPosition,
78
80
  menuShouldBlockScroll = _this$props.menuShouldBlockScroll,
79
81
  captureMenuScroll = _this$props.captureMenuScroll,
80
- closeMenuOnScroll = _this$props.closeMenuOnScroll;
82
+ closeMenuOnScroll = _this$props.closeMenuOnScroll,
83
+ loadUserSource = _this$props.loadUserSource;
81
84
  var width = this.props.width;
82
85
  var SelectComponent = allowEmail ? _select.CreatableSelect : _select.default;
83
86
  var creatableProps = suggestEmailsForDomain ? (0, _creatableEmailSuggestion.getCreatableSuggestedEmailProps)(suggestEmailsForDomain, isValidEmail) : (0, _creatable.getCreatableProps)(isValidEmail);
@@ -91,13 +94,15 @@ var UserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Component) {
91
94
  var pickerProps = allowEmail ? _objectSpread(_objectSpread(_objectSpread({}, defaultPickerProps), creatableProps), {}, {
92
95
  emailLabel: emailLabel
93
96
  }) : _objectSpread({}, defaultPickerProps);
94
- return /*#__PURE__*/_react.default.createElement(_MessagesIntlProvider.default, null, /*#__PURE__*/_react.default.createElement(_BaseUserPicker.BaseUserPickerWithoutAnalytics, (0, _extends2.default)({}, this.props, {
97
+ return /*#__PURE__*/_react.default.createElement(_MessagesIntlProvider.default, null, /*#__PURE__*/_react.default.createElement(_UserSourceProvider.ExusUserSourceProvider, {
98
+ fetchUserSource: loadUserSource
99
+ }, /*#__PURE__*/_react.default.createElement(_BaseUserPicker.BaseUserPickerWithoutAnalytics, (0, _extends2.default)({}, this.props, {
95
100
  width: width,
96
101
  SelectComponent: SelectComponent,
97
102
  styles: (0, _styles.getStyles)(width, isMulti, this.props.styles),
98
103
  components: (0, _components.getComponents)(isMulti, anchor),
99
104
  pickerProps: pickerProps
100
- })));
105
+ }))));
101
106
  }
102
107
  }]);
103
108
  return UserPickerWithoutAnalytics;
@@ -73,6 +73,11 @@ var messages = (0, _reactIntlNext.defineMessages)({
73
73
  defaultMessage: 'Found in:',
74
74
  description: 'From where the external user is coming'
75
75
  },
76
+ externalUserSourcesError: {
77
+ id: 'fabric.elements.user-picker.external.sourced.error',
78
+ defaultMessage: "We can't connect you right now.",
79
+ description: "Error message when we can't fetch a user's import sources"
80
+ },
76
81
  slackProvider: {
77
82
  id: 'fabric.elements.user-picker.slack.provider',
78
83
  defaultMessage: 'Slack',
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/user-picker",
3
- "version": "8.2.1",
3
+ "version": "8.3.0",
4
4
  "sideEffects": false
5
5
  }
@@ -0,0 +1,56 @@
1
+ import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
2
+ const ExusUserSourceContext = /*#__PURE__*/createContext({});
3
+ export const ExusUserSourceProvider = ({
4
+ fetchUserSource,
5
+ children
6
+ }) => /*#__PURE__*/React.createElement(ExusUserSourceContext.Provider, {
7
+ value: {
8
+ fetchUserSource
9
+ }
10
+ }, children);
11
+ export const useUserSource = (accountId, existingSources) => {
12
+ const {
13
+ fetchUserSource
14
+ } = useContext(ExusUserSourceContext);
15
+ const [sources, setUserSources] = useState(new Set(existingSources));
16
+ const [loading, setLoading] = useState(true);
17
+ const [error, setError] = useState(null);
18
+ const abortController = useMemo(() => {
19
+ if (typeof AbortController === 'undefined') {
20
+ return;
21
+ }
22
+
23
+ return new AbortController();
24
+ }, []);
25
+ useEffect(() => {
26
+ let isMounted = true;
27
+
28
+ const cleanup = () => {
29
+ abortController === null || abortController === void 0 ? void 0 : abortController.abort();
30
+ isMounted = false;
31
+ };
32
+
33
+ if (!fetchUserSource) {
34
+ setLoading(false);
35
+ return cleanup;
36
+ }
37
+
38
+ if (isMounted) {
39
+ fetchUserSource(accountId, abortController === null || abortController === void 0 ? void 0 : abortController.signal).then(externalSources => {
40
+ setLoading(false);
41
+ const externalSourceTypes = externalSources.map(source => source.sourceType);
42
+ setUserSources(new Set([...sources, ...externalSourceTypes]));
43
+ }).catch(error => {
44
+ setLoading(false);
45
+ setError(error);
46
+ });
47
+ }
48
+
49
+ return cleanup;
50
+ }, [fetchUserSource, accountId, sources, abortController]);
51
+ return {
52
+ sources: Array.from(sources),
53
+ loading,
54
+ error
55
+ };
56
+ };
@@ -6,6 +6,7 @@ import { AvatarItemOption, TextWrapper } from '../AvatarItemOption';
6
6
  import { SizeableAvatar } from '../SizeableAvatar';
7
7
  import EditorPanelIcon from '@atlaskit/icon/glyph/editor/panel';
8
8
  import Tooltip from '@atlaskit/tooltip';
9
+ import Spinner from '@atlaskit/spinner';
9
10
  import styled from 'styled-components';
10
11
  import { SlackIcon } from '../assets/slack';
11
12
  import { GoogleIcon } from '../assets/google';
@@ -13,6 +14,7 @@ import { MicrosoftIcon } from '../assets/microsoft';
13
14
  import { GitHubIcon } from '../assets/github';
14
15
  import { FormattedMessage } from 'react-intl-next';
15
16
  import { messages } from '../i18n';
17
+ import { ExternalUserSourcesContainer } from '../ExternalUserSourcesContainer';
16
18
  export const ImageContainer = styled.span`
17
19
  height: 12px;
18
20
  width: 12px;
@@ -94,18 +96,15 @@ export class ExternalUserOption extends React.PureComponent {
94
96
  });
95
97
  });
96
98
 
97
- _defineProperty(this, "getSourcesInfoTooltip", () => {
98
- var _this$props$user$sour;
99
-
100
- return ((_this$props$user$sour = this.props.user.sources) === null || _this$props$user$sour === void 0 ? void 0 : _this$props$user$sour.length) > 0 ? /*#__PURE__*/React.createElement(Tooltip, {
101
- content: this.formattedTooltipContent(),
102
- position: 'right-start'
103
- }, /*#__PURE__*/React.createElement(EditorPanelIcon, {
104
- label: "",
105
- size: "large",
106
- primaryColor: token('color.text.lowEmphasis', N200)
107
- })) : undefined;
108
- });
99
+ _defineProperty(this, "getSourcesInfoTooltip", () => this.props.user.isExternal ? /*#__PURE__*/React.createElement(Tooltip, {
100
+ content: this.formattedTooltipContent(),
101
+ position: 'right-start'
102
+ }, /*#__PURE__*/React.createElement(EditorPanelIcon, {
103
+ testId: "source-icon",
104
+ label: "",
105
+ size: "large",
106
+ primaryColor: token('color.text.lowEmphasis', N200)
107
+ })) : undefined);
109
108
  }
110
109
 
111
110
  render() {
@@ -118,13 +117,29 @@ export class ExternalUserOption extends React.PureComponent {
118
117
  }
119
118
 
120
119
  formattedTooltipContent() {
121
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(FormattedMessage, messages.externalUserSourcesHeading)), /*#__PURE__*/React.createElement(SourcesTooltipContainer, null, this.props.user.sources.map(s => SourcesInfoMap.get(s)).filter(s => s).map(({
120
+ const {
121
+ user: {
122
+ id,
123
+ sources
124
+ }
125
+ } = this.props;
126
+ return /*#__PURE__*/React.createElement(ExternalUserSourcesContainer, {
127
+ accountId: id,
128
+ initialSources: sources
129
+ }, ({
130
+ sources,
131
+ sourcesLoading,
132
+ sourcesError
133
+ }) => /*#__PURE__*/React.createElement(React.Fragment, null, sourcesError !== null && sources.length === 0 ? /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(FormattedMessage, messages.externalUserSourcesError)) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(FormattedMessage, messages.externalUserSourcesHeading)), /*#__PURE__*/React.createElement(SourcesTooltipContainer, null, sourcesLoading && /*#__PURE__*/React.createElement(Spinner, {
134
+ size: "small",
135
+ appearance: "invert"
136
+ }), !sourcesLoading && sources.map(s => SourcesInfoMap.get(s)).filter(s => s).map(({
122
137
  key,
123
138
  icon,
124
139
  label
125
140
  }) => /*#__PURE__*/React.createElement(SourceWrapper, {
126
141
  key: key
127
- }, /*#__PURE__*/React.createElement(ImageContainer, null, icon), /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(FormattedMessage, label))))));
142
+ }, /*#__PURE__*/React.createElement(ImageContainer, null, icon), /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(FormattedMessage, label))))))));
128
143
  }
129
144
 
130
145
  }
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import { useUserSource } from '../clients/UserSourceProvider';
3
+ export const ExternalUserSourcesContainer = ({
4
+ children,
5
+ accountId,
6
+ initialSources = []
7
+ }) => {
8
+ const {
9
+ sources,
10
+ loading: sourcesLoading,
11
+ error: sourcesError
12
+ } = useUserSource(accountId, initialSources);
13
+
14
+ if (typeof children === 'function') {
15
+ return children({
16
+ sources,
17
+ sourcesLoading,
18
+ sourcesError
19
+ });
20
+ }
21
+
22
+ return React.Children.map(children, child => /*#__PURE__*/React.cloneElement(child, {
23
+ sources,
24
+ sourcesLoading,
25
+ sourcesError
26
+ }));
27
+ };
@@ -9,6 +9,7 @@ import { getComponents } from './components';
9
9
  import { getCreatableProps } from './creatable';
10
10
  import { getCreatableSuggestedEmailProps } from './creatableEmailSuggestion';
11
11
  import MessagesIntlProvider from './MessagesIntlProvider';
12
+ import { ExusUserSourceProvider } from './../clients/UserSourceProvider';
12
13
  export class UserPickerWithoutAnalytics extends React.Component {
13
14
  render() {
14
15
  const {
@@ -22,7 +23,8 @@ export class UserPickerWithoutAnalytics extends React.Component {
22
23
  menuPosition,
23
24
  menuShouldBlockScroll,
24
25
  captureMenuScroll,
25
- closeMenuOnScroll
26
+ closeMenuOnScroll,
27
+ loadUserSource
26
28
  } = this.props;
27
29
  const width = this.props.width;
28
30
  const SelectComponent = allowEmail ? CreatableSelect : Select;
@@ -39,13 +41,15 @@ export class UserPickerWithoutAnalytics extends React.Component {
39
41
  emailLabel
40
42
  } : { ...defaultPickerProps
41
43
  };
42
- return /*#__PURE__*/React.createElement(MessagesIntlProvider, null, /*#__PURE__*/React.createElement(BaseUserPickerWithoutAnalytics, _extends({}, this.props, {
44
+ return /*#__PURE__*/React.createElement(MessagesIntlProvider, null, /*#__PURE__*/React.createElement(ExusUserSourceProvider, {
45
+ fetchUserSource: loadUserSource
46
+ }, /*#__PURE__*/React.createElement(BaseUserPickerWithoutAnalytics, _extends({}, this.props, {
43
47
  width: width,
44
48
  SelectComponent: SelectComponent,
45
49
  styles: getStyles(width, isMulti, this.props.styles),
46
50
  components: getComponents(isMulti, anchor),
47
51
  pickerProps: pickerProps
48
- })));
52
+ }))));
49
53
  }
50
54
 
51
55
  }
@@ -65,6 +65,11 @@ export const messages = defineMessages({
65
65
  defaultMessage: 'Found in:',
66
66
  description: 'From where the external user is coming'
67
67
  },
68
+ externalUserSourcesError: {
69
+ id: 'fabric.elements.user-picker.external.sourced.error',
70
+ defaultMessage: "We can't connect you right now.",
71
+ description: "Error message when we can't fetch a user's import sources"
72
+ },
68
73
  slackProvider: {
69
74
  id: 'fabric.elements.user-picker.slack.provider',
70
75
  defaultMessage: 'Slack',
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/user-picker",
3
- "version": "8.2.1",
3
+ "version": "8.3.0",
4
4
  "sideEffects": false
5
5
  }
@@ -0,0 +1,73 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
+ import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
4
+ var ExusUserSourceContext = /*#__PURE__*/createContext({});
5
+ export var ExusUserSourceProvider = function ExusUserSourceProvider(_ref) {
6
+ var fetchUserSource = _ref.fetchUserSource,
7
+ children = _ref.children;
8
+ return /*#__PURE__*/React.createElement(ExusUserSourceContext.Provider, {
9
+ value: {
10
+ fetchUserSource: fetchUserSource
11
+ }
12
+ }, children);
13
+ };
14
+ export var useUserSource = function useUserSource(accountId, existingSources) {
15
+ var _useContext = useContext(ExusUserSourceContext),
16
+ fetchUserSource = _useContext.fetchUserSource;
17
+
18
+ var _useState = useState(new Set(existingSources)),
19
+ _useState2 = _slicedToArray(_useState, 2),
20
+ sources = _useState2[0],
21
+ setUserSources = _useState2[1];
22
+
23
+ var _useState3 = useState(true),
24
+ _useState4 = _slicedToArray(_useState3, 2),
25
+ loading = _useState4[0],
26
+ setLoading = _useState4[1];
27
+
28
+ var _useState5 = useState(null),
29
+ _useState6 = _slicedToArray(_useState5, 2),
30
+ error = _useState6[0],
31
+ setError = _useState6[1];
32
+
33
+ var abortController = useMemo(function () {
34
+ if (typeof AbortController === 'undefined') {
35
+ return;
36
+ }
37
+
38
+ return new AbortController();
39
+ }, []);
40
+ useEffect(function () {
41
+ var isMounted = true;
42
+
43
+ var cleanup = function cleanup() {
44
+ abortController === null || abortController === void 0 ? void 0 : abortController.abort();
45
+ isMounted = false;
46
+ };
47
+
48
+ if (!fetchUserSource) {
49
+ setLoading(false);
50
+ return cleanup;
51
+ }
52
+
53
+ if (isMounted) {
54
+ fetchUserSource(accountId, abortController === null || abortController === void 0 ? void 0 : abortController.signal).then(function (externalSources) {
55
+ setLoading(false);
56
+ var externalSourceTypes = externalSources.map(function (source) {
57
+ return source.sourceType;
58
+ });
59
+ setUserSources(new Set([].concat(_toConsumableArray(sources), _toConsumableArray(externalSourceTypes))));
60
+ }).catch(function (error) {
61
+ setLoading(false);
62
+ setError(error);
63
+ });
64
+ }
65
+
66
+ return cleanup;
67
+ }, [fetchUserSource, accountId, sources, abortController]);
68
+ return {
69
+ sources: Array.from(sources),
70
+ loading: loading,
71
+ error: error
72
+ };
73
+ };
@@ -21,6 +21,7 @@ import { AvatarItemOption, TextWrapper } from '../AvatarItemOption';
21
21
  import { SizeableAvatar } from '../SizeableAvatar';
22
22
  import EditorPanelIcon from '@atlaskit/icon/glyph/editor/panel';
23
23
  import Tooltip from '@atlaskit/tooltip';
24
+ import Spinner from '@atlaskit/spinner';
24
25
  import styled from 'styled-components';
25
26
  import { SlackIcon } from '../assets/slack';
26
27
  import { GoogleIcon } from '../assets/google';
@@ -28,6 +29,7 @@ import { MicrosoftIcon } from '../assets/microsoft';
28
29
  import { GitHubIcon } from '../assets/github';
29
30
  import { FormattedMessage } from 'react-intl-next';
30
31
  import { messages } from '../i18n';
32
+ import { ExternalUserSourcesContainer } from '../ExternalUserSourcesContainer';
31
33
  export var ImageContainer = styled.span(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n height: 12px;\n width: 12px;\n padding-right: 4px;\n"])));
32
34
  export var SourcesTooltipContainer = styled.div(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n padding-bottom: 4px;\n"])));
33
35
  export var SourceWrapper = styled.div(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\n padding-top: 4px;\n display: flex;\n align-items: center;\n"])));
@@ -106,12 +108,11 @@ export var ExternalUserOption = /*#__PURE__*/function (_React$PureComponent) {
106
108
  });
107
109
 
108
110
  _defineProperty(_assertThisInitialized(_this), "getSourcesInfoTooltip", function () {
109
- var _this$props$user$sour;
110
-
111
- return ((_this$props$user$sour = _this.props.user.sources) === null || _this$props$user$sour === void 0 ? void 0 : _this$props$user$sour.length) > 0 ? /*#__PURE__*/React.createElement(Tooltip, {
111
+ return _this.props.user.isExternal ? /*#__PURE__*/React.createElement(Tooltip, {
112
112
  content: _this.formattedTooltipContent(),
113
113
  position: 'right-start'
114
114
  }, /*#__PURE__*/React.createElement(EditorPanelIcon, {
115
+ testId: "source-icon",
115
116
  label: "",
116
117
  size: "large",
117
118
  primaryColor: token('color.text.lowEmphasis', N200)
@@ -134,18 +135,32 @@ export var ExternalUserOption = /*#__PURE__*/function (_React$PureComponent) {
134
135
  }, {
135
136
  key: "formattedTooltipContent",
136
137
  value: function formattedTooltipContent() {
137
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(FormattedMessage, messages.externalUserSourcesHeading)), /*#__PURE__*/React.createElement(SourcesTooltipContainer, null, this.props.user.sources.map(function (s) {
138
- return SourcesInfoMap.get(s);
139
- }).filter(function (s) {
140
- return s;
141
- }).map(function (_ref) {
142
- var key = _ref.key,
143
- icon = _ref.icon,
144
- label = _ref.label;
145
- return /*#__PURE__*/React.createElement(SourceWrapper, {
146
- key: key
147
- }, /*#__PURE__*/React.createElement(ImageContainer, null, icon), /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(FormattedMessage, label)));
148
- })));
138
+ var _this$props$user2 = this.props.user,
139
+ id = _this$props$user2.id,
140
+ sources = _this$props$user2.sources;
141
+ return /*#__PURE__*/React.createElement(ExternalUserSourcesContainer, {
142
+ accountId: id,
143
+ initialSources: sources
144
+ }, function (_ref) {
145
+ var sources = _ref.sources,
146
+ sourcesLoading = _ref.sourcesLoading,
147
+ sourcesError = _ref.sourcesError;
148
+ return /*#__PURE__*/React.createElement(React.Fragment, null, sourcesError !== null && sources.length === 0 ? /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(FormattedMessage, messages.externalUserSourcesError)) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(FormattedMessage, messages.externalUserSourcesHeading)), /*#__PURE__*/React.createElement(SourcesTooltipContainer, null, sourcesLoading && /*#__PURE__*/React.createElement(Spinner, {
149
+ size: "small",
150
+ appearance: "invert"
151
+ }), !sourcesLoading && sources.map(function (s) {
152
+ return SourcesInfoMap.get(s);
153
+ }).filter(function (s) {
154
+ return s;
155
+ }).map(function (_ref2) {
156
+ var key = _ref2.key,
157
+ icon = _ref2.icon,
158
+ label = _ref2.label;
159
+ return /*#__PURE__*/React.createElement(SourceWrapper, {
160
+ key: key
161
+ }, /*#__PURE__*/React.createElement(ImageContainer, null, icon), /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(FormattedMessage, label)));
162
+ }))));
163
+ });
149
164
  }
150
165
  }]);
151
166
 
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import { useUserSource } from '../clients/UserSourceProvider';
3
+ export var ExternalUserSourcesContainer = function ExternalUserSourcesContainer(_ref) {
4
+ var children = _ref.children,
5
+ accountId = _ref.accountId,
6
+ _ref$initialSources = _ref.initialSources,
7
+ initialSources = _ref$initialSources === void 0 ? [] : _ref$initialSources;
8
+
9
+ var _useUserSource = useUserSource(accountId, initialSources),
10
+ sources = _useUserSource.sources,
11
+ sourcesLoading = _useUserSource.loading,
12
+ sourcesError = _useUserSource.error;
13
+
14
+ if (typeof children === 'function') {
15
+ return children({
16
+ sources: sources,
17
+ sourcesLoading: sourcesLoading,
18
+ sourcesError: sourcesError
19
+ });
20
+ }
21
+
22
+ return React.Children.map(children, function (child) {
23
+ return /*#__PURE__*/React.cloneElement(child, {
24
+ sources: sources,
25
+ sourcesLoading: sourcesLoading,
26
+ sourcesError: sourcesError
27
+ });
28
+ });
29
+ };
@@ -23,6 +23,7 @@ import { getComponents } from './components';
23
23
  import { getCreatableProps } from './creatable';
24
24
  import { getCreatableSuggestedEmailProps } from './creatableEmailSuggestion';
25
25
  import MessagesIntlProvider from './MessagesIntlProvider';
26
+ import { ExusUserSourceProvider } from './../clients/UserSourceProvider';
26
27
  export var UserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Component) {
27
28
  _inherits(UserPickerWithoutAnalytics, _React$Component);
28
29
 
@@ -48,7 +49,8 @@ export var UserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Component)
48
49
  menuPosition = _this$props.menuPosition,
49
50
  menuShouldBlockScroll = _this$props.menuShouldBlockScroll,
50
51
  captureMenuScroll = _this$props.captureMenuScroll,
51
- closeMenuOnScroll = _this$props.closeMenuOnScroll;
52
+ closeMenuOnScroll = _this$props.closeMenuOnScroll,
53
+ loadUserSource = _this$props.loadUserSource;
52
54
  var width = this.props.width;
53
55
  var SelectComponent = allowEmail ? CreatableSelect : Select;
54
56
  var creatableProps = suggestEmailsForDomain ? getCreatableSuggestedEmailProps(suggestEmailsForDomain, isValidEmail) : getCreatableProps(isValidEmail);
@@ -62,13 +64,15 @@ export var UserPickerWithoutAnalytics = /*#__PURE__*/function (_React$Component)
62
64
  var pickerProps = allowEmail ? _objectSpread(_objectSpread(_objectSpread({}, defaultPickerProps), creatableProps), {}, {
63
65
  emailLabel: emailLabel
64
66
  }) : _objectSpread({}, defaultPickerProps);
65
- return /*#__PURE__*/React.createElement(MessagesIntlProvider, null, /*#__PURE__*/React.createElement(BaseUserPickerWithoutAnalytics, _extends({}, this.props, {
67
+ return /*#__PURE__*/React.createElement(MessagesIntlProvider, null, /*#__PURE__*/React.createElement(ExusUserSourceProvider, {
68
+ fetchUserSource: loadUserSource
69
+ }, /*#__PURE__*/React.createElement(BaseUserPickerWithoutAnalytics, _extends({}, this.props, {
66
70
  width: width,
67
71
  SelectComponent: SelectComponent,
68
72
  styles: getStyles(width, isMulti, this.props.styles),
69
73
  components: getComponents(isMulti, anchor),
70
74
  pickerProps: pickerProps
71
- })));
75
+ }))));
72
76
  }
73
77
  }]);
74
78
 
@@ -65,6 +65,11 @@ export var messages = defineMessages({
65
65
  defaultMessage: 'Found in:',
66
66
  description: 'From where the external user is coming'
67
67
  },
68
+ externalUserSourcesError: {
69
+ id: 'fabric.elements.user-picker.external.sourced.error',
70
+ defaultMessage: "We can't connect you right now.",
71
+ description: "Error message when we can't fetch a user's import sources"
72
+ },
68
73
  slackProvider: {
69
74
  id: 'fabric.elements.user-picker.slack.provider',
70
75
  defaultMessage: 'Slack',
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/user-picker",
3
- "version": "8.2.1",
3
+ "version": "8.3.0",
4
4
  "sideEffects": false
5
5
  }
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { LoadUserSource, UserSource } from '../types';
3
+ export interface UserSourceContext {
4
+ fetchUserSource?: LoadUserSource;
5
+ }
6
+ export declare const ExusUserSourceProvider: React.FC<UserSourceContext>;
7
+ export declare const useUserSource: (accountId: string, existingSources?: UserSource[] | undefined) => {
8
+ sources: UserSource[];
9
+ loading: boolean;
10
+ error: string | null;
11
+ };
@@ -48,7 +48,7 @@ export declare class BaseUserPickerWithoutAnalytics extends React.Component<Base
48
48
  private getAppearance;
49
49
  render(): JSX.Element;
50
50
  }
51
- export declare const BaseUserPicker: React.ForwardRefExoticComponent<Pick<Pick<BaseUserPickerProps, "placeholder" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "components" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "isMulti" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "fieldId" | "width" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions" | "SelectComponent" | "pickerProps">, "placeholder" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "components" | "inputId" | "isDisabled" | "isLoading" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "fieldId" | "width" | "menuMinWidth" | "maxPickerHeight" | "loadOptions" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions" | "SelectComponent" | "pickerProps"> & Partial<Pick<Pick<BaseUserPickerProps, "placeholder" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "components" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "isMulti" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "fieldId" | "width" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions" | "SelectComponent" | "pickerProps">, "isClearable" | "isMulti" | "textFieldBackgroundColor" | "subtle" | "noBorder">> & Partial<Pick<{
51
+ export declare const BaseUserPicker: React.ForwardRefExoticComponent<Pick<Pick<BaseUserPickerProps, "placeholder" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "components" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "isMulti" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "fieldId" | "width" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "loadUserSource" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions" | "SelectComponent" | "pickerProps">, "placeholder" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "components" | "inputId" | "isDisabled" | "isLoading" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "fieldId" | "width" | "menuMinWidth" | "maxPickerHeight" | "loadOptions" | "loadUserSource" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions" | "SelectComponent" | "pickerProps"> & Partial<Pick<Pick<BaseUserPickerProps, "placeholder" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "components" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "isMulti" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "fieldId" | "width" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "loadUserSource" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions" | "SelectComponent" | "pickerProps">, "isClearable" | "isMulti" | "textFieldBackgroundColor" | "subtle" | "noBorder">> & Partial<Pick<{
52
52
  isMulti: boolean;
53
53
  subtle: boolean;
54
54
  noBorder: boolean;
@@ -0,0 +1,14 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { UserSource } from '../types';
3
+ declare type SourcesChildrenFunc = ({ sources, sourcesLoading, sourcesError, }: {
4
+ sources: UserSource[];
5
+ sourcesLoading: boolean;
6
+ sourcesError: string | null;
7
+ }) => ReactNode;
8
+ interface SourcesContainerProps {
9
+ accountId: string;
10
+ initialSources: UserSource[];
11
+ children: SourcesChildrenFunc;
12
+ }
13
+ export declare const ExternalUserSourcesContainer: React.FC<SourcesContainerProps>;
14
+ export {};
@@ -27,7 +27,7 @@ export declare class PopupUserPickerWithoutAnalytics extends React.Component<Pop
27
27
  };
28
28
  render(): JSX.Element;
29
29
  }
30
- export declare const PopupUserPicker: React.ForwardRefExoticComponent<Pick<Pick<PopupUserPickerProps, "placeholder" | "target" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "isMulti" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "offset" | "fieldId" | "width" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions" | "popupTitle" | "boundariesElement" | "placement" | "rootBoundary" | "shouldFlip">, "placeholder" | "target" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "fieldId" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions" | "popupTitle"> & Partial<Pick<Pick<PopupUserPickerProps, "placeholder" | "target" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "isMulti" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "offset" | "fieldId" | "width" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions" | "popupTitle" | "boundariesElement" | "placement" | "rootBoundary" | "shouldFlip">, "isMulti" | "offset" | "width" | "boundariesElement" | "placement" | "rootBoundary" | "shouldFlip">> & Partial<Pick<{
30
+ export declare const PopupUserPicker: React.ForwardRefExoticComponent<Pick<Pick<PopupUserPickerProps, "placeholder" | "target" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "isMulti" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "offset" | "fieldId" | "width" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "loadUserSource" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions" | "popupTitle" | "boundariesElement" | "placement" | "rootBoundary" | "shouldFlip">, "placeholder" | "target" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "fieldId" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "loadUserSource" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions" | "popupTitle"> & Partial<Pick<Pick<PopupUserPickerProps, "placeholder" | "target" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "isMulti" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "offset" | "fieldId" | "width" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "loadUserSource" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions" | "popupTitle" | "boundariesElement" | "placement" | "rootBoundary" | "shouldFlip">, "isMulti" | "offset" | "width" | "boundariesElement" | "placement" | "rootBoundary" | "shouldFlip">> & Partial<Pick<{
31
31
  boundariesElement: string;
32
32
  width: number;
33
33
  isMulti: boolean;
@@ -7,7 +7,7 @@ export declare class UserPickerWithoutAnalytics extends React.Component<UserPick
7
7
  };
8
8
  render(): JSX.Element;
9
9
  }
10
- export declare const UserPicker: React.ForwardRefExoticComponent<Pick<Pick<UserPickerProps, "placeholder" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "isMulti" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "fieldId" | "width" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions">, "placeholder" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "fieldId" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions"> & Partial<Pick<Pick<UserPickerProps, "placeholder" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "isMulti" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "fieldId" | "width" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions">, "isMulti" | "width">> & Partial<Pick<{
10
+ export declare const UserPicker: React.ForwardRefExoticComponent<Pick<Pick<UserPickerProps, "placeholder" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "isMulti" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "fieldId" | "width" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "loadUserSource" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions">, "placeholder" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "fieldId" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "loadUserSource" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions"> & Partial<Pick<Pick<UserPickerProps, "placeholder" | "autoFocus" | "captureMenuScroll" | "closeMenuOnScroll" | "inputId" | "isClearable" | "isDisabled" | "isLoading" | "isMulti" | "menuPosition" | "menuPortalTarget" | "menuShouldBlockScroll" | "noOptionsMessage" | "onBlur" | "onChange" | "onFocus" | "onInputChange" | "options" | "styles" | "value" | "defaultValue" | "fieldId" | "width" | "menuMinWidth" | "maxPickerHeight" | "textFieldBackgroundColor" | "loadOptions" | "loadUserSource" | "search" | "anchor" | "open" | "onSelection" | "onClear" | "onClose" | "appearance" | "subtle" | "noBorder" | "addMoreMessage" | "clearValueLabel" | "allowEmail" | "suggestEmailsForDomain" | "emailLabel" | "disableInput" | "isValidEmail" | "maxOptions">, "isMulti" | "width">> & Partial<Pick<{
11
11
  width: number;
12
12
  isMulti: boolean;
13
13
  }, never>> & React.RefAttributes<any>>;
@@ -64,6 +64,11 @@ export declare const messages: {
64
64
  defaultMessage: string;
65
65
  description: string;
66
66
  };
67
+ externalUserSourcesError: {
68
+ id: string;
69
+ defaultMessage: string;
70
+ description: string;
71
+ };
67
72
  slackProvider: {
68
73
  id: string;
69
74
  defaultMessage: string;
@@ -39,6 +39,13 @@ export declare type UserPickerProps = WithAnalyticsEventsProps & {
39
39
  * sessionId?: user picker session identifier, used to track success metric for users providers
40
40
  */
41
41
  loadOptions?: LoadOptions;
42
+ /**
43
+ * Function used to load user source if they are an external user.
44
+ * accepts two params:
45
+ * accountId: account ID of the user to lookup sources
46
+ * signal: AbortController signal to abort the request if the tooltip is closed
47
+ */
48
+ loadUserSource?: LoadUserSource;
42
49
  /** Callback for value change events fired whenever a selection is inserted or removed. */
43
50
  onChange?: OnChange;
44
51
  /** To enable multi user picker. */
@@ -253,6 +260,13 @@ export declare type Option<Data = OptionData> = {
253
260
  value: string;
254
261
  data: Data;
255
262
  };
263
+ export interface UserSourceResult {
264
+ sourceId: string;
265
+ sourceType: UserSource;
266
+ }
267
+ export interface LoadUserSource {
268
+ (accountId: string, signal?: AbortSignal): Promise<UserSourceResult[]>;
269
+ }
256
270
  export interface LoadOptions {
257
271
  (searchText?: string, sessionId?: string): Promisable<OptionData | OptionData[]> | Iterable<Promisable<OptionData[] | OptionData> | OptionData | OptionData[]>;
258
272
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/user-picker",
3
- "version": "8.2.1",
3
+ "version": "8.3.0",
4
4
  "description": "Fabric component for display a dropdown to select a user from",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -32,6 +32,7 @@
32
32
  "@atlaskit/lozenge": "^11.0.0",
33
33
  "@atlaskit/popper": "^5.0.0",
34
34
  "@atlaskit/select": "^15.2.0",
35
+ "@atlaskit/spinner": "^15.1.4",
35
36
  "@atlaskit/theme": "^12.0.0",
36
37
  "@atlaskit/tokens": "^0.4.0",
37
38
  "@atlaskit/tooltip": "^17.5.0",
@@ -52,11 +53,17 @@
52
53
  "@atlaskit/docs": "*",
53
54
  "@atlaskit/elements-test-helpers": "^0.7.0",
54
55
  "@atlaskit/modal-dialog": "^12.2.0",
56
+ "@atlaskit/radio": "^5.3.4",
55
57
  "@atlaskit/range": "^5.0.11",
56
58
  "@atlaskit/section-message": "^6.0.0",
57
59
  "@atlaskit/textfield": "^5.0.0",
58
60
  "@atlaskit/util-data-test": "^17.0.0",
59
61
  "@atlassian/atlassian-frontend-prettier-config-1.0.1": "npm:@atlassian/atlassian-frontend-prettier-config@1.0.1",
62
+ "@testing-library/dom": "^7.7.3",
63
+ "@testing-library/jest-dom": "^4.1.0",
64
+ "@testing-library/react": "^8.0.1",
65
+ "@testing-library/react-hooks": "^1.0.4",
66
+ "@testing-library/user-event": "10.4.0",
60
67
  "@types/uuid": "^3.4.4",
61
68
  "enzyme": "^3.10.0",
62
69
  "enzyme-react-intl": "^2.0.6",