@atlaskit/link-picker 1.9.2 → 1.9.4

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/analytics.codegen.js +1 -1
  3. package/dist/cjs/common/analytics/context.js +137 -0
  4. package/dist/cjs/common/analytics/index.js +27 -0
  5. package/dist/cjs/common/analytics/input-field-tracking.js +115 -0
  6. package/dist/cjs/common/constants.js +1 -1
  7. package/dist/cjs/ui/error-boundary/index.js +104 -0
  8. package/dist/cjs/ui/index.js +10 -6
  9. package/dist/cjs/ui/link-picker/index.js +56 -16
  10. package/dist/cjs/ui/link-picker/text-input/index.js +18 -23
  11. package/dist/cjs/version.json +1 -1
  12. package/dist/es2019/analytics.codegen.js +1 -1
  13. package/dist/es2019/common/analytics/context.js +95 -0
  14. package/dist/es2019/common/analytics/index.js +2 -0
  15. package/dist/es2019/common/analytics/input-field-tracking.js +85 -0
  16. package/dist/es2019/common/constants.js +1 -1
  17. package/dist/es2019/ui/error-boundary/index.js +62 -0
  18. package/dist/es2019/ui/index.js +8 -5
  19. package/dist/es2019/ui/link-picker/index.js +51 -14
  20. package/dist/es2019/ui/link-picker/link-search-error/index.js +2 -2
  21. package/dist/es2019/ui/link-picker/text-input/index.js +14 -20
  22. package/dist/es2019/version.json +1 -1
  23. package/dist/esm/analytics.codegen.js +1 -1
  24. package/dist/esm/common/analytics/context.js +110 -0
  25. package/dist/esm/common/analytics/index.js +2 -0
  26. package/dist/esm/common/analytics/input-field-tracking.js +91 -0
  27. package/dist/esm/common/constants.js +1 -1
  28. package/dist/esm/ui/error-boundary/index.js +81 -0
  29. package/dist/esm/ui/index.js +8 -5
  30. package/dist/esm/ui/link-picker/index.js +54 -14
  31. package/dist/esm/ui/link-picker/link-search-error/index.js +2 -2
  32. package/dist/esm/ui/link-picker/text-input/index.js +16 -21
  33. package/dist/esm/version.json +1 -1
  34. package/dist/types/analytics.codegen.d.ts +21 -1
  35. package/dist/types/common/analytics/context.d.ts +28 -0
  36. package/dist/types/common/analytics/index.d.ts +2 -0
  37. package/dist/types/common/analytics/input-field-tracking.d.ts +17 -0
  38. package/dist/types/common/constants.d.ts +1 -1
  39. package/dist/types/ui/error-boundary/index.d.ts +27 -0
  40. package/dist/types/ui/index.d.ts +2 -5
  41. package/dist/types/ui/link-picker/text-input/index.d.ts +6 -8
  42. package/package.json +1 -1
  43. package/dist/cjs/services/with-link-picker-analytics/index.js +0 -33
  44. package/dist/es2019/services/with-link-picker-analytics/index.js +0 -15
  45. package/dist/esm/services/with-link-picker-analytics/index.js +0 -17
  46. package/dist/types/services/with-link-picker-analytics/index.d.ts +0 -4
@@ -0,0 +1,110 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
+
5
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
6
+
7
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
8
+
9
+ import React, { useCallback, useContext, useMemo, useRef, useState } from 'react';
10
+ import { AnalyticsListener } from '@atlaskit/analytics-next';
11
+ import { ANALYTICS_CHANNEL } from '../constants';
12
+ import { normalizeUrl } from '../utils/url';
13
+ var DEFAULT_CONTEXT_ATTRIBUTES = {
14
+ linkState: 'newLink',
15
+ linkFieldContent: null,
16
+ linkFieldContentInputMethod: null,
17
+ displayTextFieldContent: null,
18
+ displayTextFieldContentInputMethod: null
19
+ };
20
+ var LinkPickerAnalyticsContext = /*#__PURE__*/React.createContext({
21
+ trackAttribute: function trackAttribute() {},
22
+ trackAttributes: function trackAttributes() {},
23
+ getAttributes: function getAttributes() {
24
+ return DEFAULT_CONTEXT_ATTRIBUTES;
25
+ }
26
+ });
27
+ /**
28
+ * TODO: Should we hook this into the form state/reducer?
29
+ */
30
+
31
+ var contextAttributesFromIntialProps = function contextAttributesFromIntialProps(props) {
32
+ return {
33
+ linkState: normalizeUrl(props.url) ? 'editLink' : 'newLink',
34
+ linkFieldContent: normalizeUrl(props.url) ? 'url' : null,
35
+ displayTextFieldContent: Boolean(props.displayText) ? 'text_string' : null
36
+ };
37
+ };
38
+ /**
39
+ * Context for tracking attributes to deliver at a context-level
40
+ */
41
+
42
+
43
+ var LinkPickerAnalytics = function LinkPickerAnalytics(_ref) {
44
+ var initialContext = _ref.initialContext,
45
+ children = _ref.children;
46
+ var dataRef = useRef(_objectSpread(_objectSpread({}, DEFAULT_CONTEXT_ATTRIBUTES), initialContext));
47
+ var methods = useMemo(function () {
48
+ return {
49
+ trackAttribute: function trackAttribute(key, value) {
50
+ dataRef.current[key] = value;
51
+ },
52
+ trackAttributes: function trackAttributes(attributes) {
53
+ dataRef.current = _objectSpread(_objectSpread({}, dataRef.current), attributes);
54
+ },
55
+ getAttributes: function getAttributes() {
56
+ return dataRef.current;
57
+ }
58
+ };
59
+ }, []);
60
+ return /*#__PURE__*/React.createElement(LinkPickerAnalyticsContext.Provider, {
61
+ value: methods
62
+ }, children);
63
+ };
64
+ /**
65
+ * Hook that exposes the context-level attribute getters and setters.
66
+ */
67
+
68
+
69
+ export var useLinkPickerAnalytics = function useLinkPickerAnalytics() {
70
+ return useContext(LinkPickerAnalyticsContext);
71
+ };
72
+ /**
73
+ * Wrap component in "attributes" context store and initialise the initial context attributes from props.
74
+ */
75
+
76
+ function withLinkPickerAnalytics(WrappedComponent) {
77
+ return function (props) {
78
+ var _useState = useState(function () {
79
+ return contextAttributesFromIntialProps(props);
80
+ }),
81
+ _useState2 = _slicedToArray(_useState, 1),
82
+ initialContext = _useState2[0];
83
+
84
+ return /*#__PURE__*/React.createElement(LinkPickerAnalytics, _extends({
85
+ initialContext: initialContext
86
+ }, props), /*#__PURE__*/React.createElement(WrappedComponent, props));
87
+ };
88
+ }
89
+ /**
90
+ * Wraps a component with the analytics context + listener to update events with contextual-level attributes.
91
+ * Should be implemented once at the root of the link picker.
92
+ */
93
+
94
+
95
+ export function withLinkPickerAnalyticsContext(WrappedComponent) {
96
+ return withLinkPickerAnalytics(function (props) {
97
+ var _useLinkPickerAnalyti = useLinkPickerAnalytics(),
98
+ getAttributes = _useLinkPickerAnalyti.getAttributes;
99
+
100
+ var onEvent = useCallback(function (event) {
101
+ event.update({
102
+ attributes: getAttributes()
103
+ });
104
+ }, [getAttributes]);
105
+ return /*#__PURE__*/React.createElement(AnalyticsListener, {
106
+ channel: ANALYTICS_CHANNEL,
107
+ onEvent: onEvent
108
+ }, /*#__PURE__*/React.createElement(WrappedComponent, props));
109
+ });
110
+ }
@@ -0,0 +1,2 @@
1
+ export { useLinkPickerAnalytics, withLinkPickerAnalyticsContext } from './context';
2
+ export { withInputFieldTracking } from './input-field-tracking';
@@ -0,0 +1,91 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
+ import _typeof from "@babel/runtime/helpers/typeof";
4
+ import React, { useCallback, useRef } from 'react';
5
+ import { useAnalyticsEvents } from '@atlaskit/analytics-next';
6
+ import { ANALYTICS_CHANNEL } from '../constants';
7
+ import createEventPayload from '../../analytics.codegen';
8
+ import { useLinkPickerAnalytics } from '.';
9
+
10
+ var isEventWithInputType = function isEventWithInputType(e) {
11
+ return _typeof(e) === 'object' && e !== null && 'inputType' in e;
12
+ };
13
+
14
+ var getChangeEventInputMethodType = function getChangeEventInputMethodType(event) {
15
+ if (isEventWithInputType(event.nativeEvent) && typeof event.nativeEvent.inputType === 'string' && event.nativeEvent.inputType.includes('insertFromPaste')) {
16
+ return 'paste';
17
+ }
18
+
19
+ return 'manual';
20
+ };
21
+ /**
22
+ * Wraps an input field and connects it to the link picker analytics context to provide a baseline of analytics tracking
23
+ * @param WrappedComponent The input field to be tracked
24
+ * @param field The name of the field (ie matching actionSubjectId)
25
+ * @param filterOnChangeEventAttrs Function to hook into and filter/customise the default attribute tracking logic
26
+ * @returns HOC Input field with tracking
27
+ */
28
+
29
+
30
+ export var withInputFieldTracking = function withInputFieldTracking(WrappedComponent, field) {
31
+ var filterOnChangeEventAttrs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function (_, attributes) {
32
+ return attributes;
33
+ };
34
+ return function (props) {
35
+ var onBlur = props.onBlur,
36
+ onChange = props.onChange,
37
+ onClear = props.onClear,
38
+ onFocus = props.onFocus;
39
+
40
+ var _useLinkPickerAnalyti = useLinkPickerAnalytics(),
41
+ trackAttribute = _useLinkPickerAnalyti.trackAttribute,
42
+ trackAttributes = _useLinkPickerAnalyti.trackAttributes;
43
+
44
+ var _useAnalyticsEvents = useAnalyticsEvents(),
45
+ createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent; // Ref to track if the value of the field has changed between focus and blur
46
+
47
+
48
+ var valueOnFocus = useRef(null); // Track the content and input method on every direct change to the input
49
+
50
+ var handleOnChange = useCallback(function (event) {
51
+ var _filterOnChangeEventA;
52
+
53
+ // TODO: See above re template literal, improve typesafety here
54
+ // trackAttribute(`${field}FieldContent`, contentTypeFn(event.currentTarget.value));
55
+ // trackAttribute(`${field}FieldContentInputMethod`, methodTypeFn(event));
56
+ trackAttributes(filterOnChangeEventAttrs(event, (_filterOnChangeEventA = {}, _defineProperty(_filterOnChangeEventA, "".concat(field, "FieldContent"), Boolean(event.currentTarget.value) ? 'text_string' : null), _defineProperty(_filterOnChangeEventA, "".concat(field, "FieldContentInputMethod"), getChangeEventInputMethodType(event)), _filterOnChangeEventA)));
57
+ onChange === null || onChange === void 0 ? void 0 : onChange(event);
58
+ }, [onChange, trackAttributes]); // Store the value of the field at the time the field is focused so we can know if it changes when the user's focus leaves
59
+
60
+ var handleOnFocus = useCallback(function (event) {
61
+ valueOnFocus.current = event.currentTarget.value;
62
+ onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
63
+ }, [onFocus]); // Fire updated events only when the user's focus leaves the field
64
+
65
+ var handleOnBlur = useCallback(function (event) {
66
+ // Only fire if the value has changed since the field was focused (we are only concerned with the net change to the field)
67
+ if (event.currentTarget.value !== valueOnFocus.current) {
68
+ createAnalyticsEvent(createEventPayload("ui.textField.updated.".concat(field, "Field"), {})).fire(ANALYTICS_CHANNEL);
69
+ }
70
+
71
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
72
+ }, [onBlur, createAnalyticsEvent]); // Track the values as null when the text field is cleared
73
+
74
+ var handleOnClear = useCallback(function () {
75
+ trackAttribute("".concat(field, "FieldContent"), null);
76
+ trackAttribute("".concat(field, "FieldContentInputMethod"), null);
77
+
78
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
79
+ args[_key] = arguments[_key];
80
+ }
81
+
82
+ onClear === null || onClear === void 0 ? void 0 : onClear.apply(void 0, args);
83
+ }, [onClear, trackAttribute]);
84
+ return /*#__PURE__*/React.createElement(WrappedComponent, _extends({}, props, {
85
+ onBlur: handleOnBlur,
86
+ onChange: handleOnChange,
87
+ onClear: handleOnClear,
88
+ onFocus: handleOnFocus
89
+ }));
90
+ };
91
+ };
@@ -1,2 +1,2 @@
1
1
  export var ANALYTICS_CHANNEL = 'media';
2
- export var COMPONENT_NAME = 'LinkPicker';
2
+ export var COMPONENT_NAME = 'linkPicker';
@@ -0,0 +1,81 @@
1
+ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
2
+ import _createClass from "@babel/runtime/helpers/createClass";
3
+ import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
4
+ import _inherits from "@babel/runtime/helpers/inherits";
5
+ import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
6
+ import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
7
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
8
+
9
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
10
+
11
+ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
12
+
13
+ import React, { Component } from 'react';
14
+ import { withAnalyticsEvents } from '@atlaskit/analytics-next';
15
+ import createEventPayload from '../../analytics.codegen';
16
+ // eslint-disable-next-line @repo/internal/react/no-class-components
17
+ export var BaseErrorBoundary = /*#__PURE__*/function (_Component) {
18
+ _inherits(BaseErrorBoundary, _Component);
19
+
20
+ var _super = _createSuper(BaseErrorBoundary);
21
+
22
+ function BaseErrorBoundary(props) {
23
+ var _this;
24
+
25
+ _classCallCheck(this, BaseErrorBoundary);
26
+
27
+ _this = _super.call(this, props);
28
+
29
+ _defineProperty(_assertThisInitialized(_this), "fireAnalytics", function (analyticsErrorPayload) {
30
+ var _window, _window$navigator, _analyticsErrorPayloa, _analyticsErrorPayloa2;
31
+
32
+ var _this$props = _this.props,
33
+ createAnalyticsEvent = _this$props.createAnalyticsEvent,
34
+ channel = _this$props.channel;
35
+ createAnalyticsEvent(createEventPayload('ui.linkPicker.unhandledErrorCaught', {
36
+ browserInfo: ((_window = window) === null || _window === void 0 ? void 0 : (_window$navigator = _window.navigator) === null || _window$navigator === void 0 ? void 0 : _window$navigator.userAgent) || 'unknown',
37
+ error: analyticsErrorPayload.error.toString(),
38
+ componentStack: (_analyticsErrorPayloa = (_analyticsErrorPayloa2 = analyticsErrorPayload.info) === null || _analyticsErrorPayloa2 === void 0 ? void 0 : _analyticsErrorPayloa2.componentStack) !== null && _analyticsErrorPayloa !== void 0 ? _analyticsErrorPayloa : ''
39
+ })).fire(channel);
40
+ });
41
+
42
+ _this.state = {
43
+ hasError: false
44
+ };
45
+ return _this;
46
+ }
47
+
48
+ _createClass(BaseErrorBoundary, [{
49
+ key: "componentDidCatch",
50
+ value: function componentDidCatch(error, info) {
51
+ var onError = this.props.onError;
52
+ var payload = {
53
+ error: error,
54
+ info: info
55
+ };
56
+ this.fireAnalytics(payload);
57
+ onError && onError(error, info);
58
+ this.setState({
59
+ hasError: true
60
+ });
61
+ }
62
+ }, {
63
+ key: "render",
64
+ value: function render() {
65
+ var _this$props2 = this.props,
66
+ children = _this$props2.children,
67
+ ErrorComponent = _this$props2.ErrorComponent;
68
+ var hasError = this.state.hasError;
69
+
70
+ if (hasError && ErrorComponent) {
71
+ return /*#__PURE__*/React.createElement(ErrorComponent, null);
72
+ }
73
+
74
+ return children;
75
+ }
76
+ }]);
77
+
78
+ return BaseErrorBoundary;
79
+ }(Component);
80
+ var ErrorBoundary = withAnalyticsEvents()(BaseErrorBoundary);
81
+ export default ErrorBoundary;
@@ -1,14 +1,16 @@
1
1
  import React, { memo } from 'react';
2
- import { AnalyticsErrorBoundary } from '@atlaskit/analytics-next';
3
2
  import { LazySuspense, lazyForPaint } from 'react-loosely-lazy';
3
+ import { AnalyticsContext } from '@atlaskit/analytics-next';
4
4
  import { COMPONENT_NAME, ANALYTICS_CHANNEL } from '../common/constants';
5
5
  import { name as packageName, version as packageVersion } from '../version.json';
6
+ import ErrorBoundary from './error-boundary';
6
7
  import { ErrorBoundaryFallback } from './error-boundary-fallback';
7
8
  import { LoaderFallback } from './loader-fallback';
8
9
  export var PACKAGE_DATA = {
9
10
  packageName: packageName,
10
11
  packageVersion: packageVersion,
11
- componentName: COMPONENT_NAME
12
+ componentName: COMPONENT_NAME,
13
+ source: COMPONENT_NAME
12
14
  };
13
15
  var LazyLinkPicker = lazyForPaint(function () {
14
16
  return import(
@@ -16,11 +18,12 @@ var LazyLinkPicker = lazyForPaint(function () {
16
18
  './link-picker');
17
19
  });
18
20
  export var ComposedLinkPicker = /*#__PURE__*/memo(function (props) {
19
- return /*#__PURE__*/React.createElement(AnalyticsErrorBoundary, {
21
+ return /*#__PURE__*/React.createElement(AnalyticsContext, {
22
+ data: PACKAGE_DATA
23
+ }, /*#__PURE__*/React.createElement(ErrorBoundary, {
20
24
  channel: ANALYTICS_CHANNEL,
21
- data: PACKAGE_DATA,
22
25
  ErrorComponent: ErrorBoundaryFallback
23
26
  }, /*#__PURE__*/React.createElement(LazySuspense, {
24
27
  fallback: LoaderFallback
25
- }, /*#__PURE__*/React.createElement(LazyLinkPicker, props)));
28
+ }, /*#__PURE__*/React.createElement(LazyLinkPicker, props))));
26
29
  });
@@ -16,13 +16,13 @@ import Tabs, { Tab, TabList } from '@atlaskit/tabs';
16
16
  import VisuallyHidden from '@atlaskit/visually-hidden';
17
17
  import createEventPayload from '../../analytics.codegen';
18
18
  import { ANALYTICS_CHANNEL } from '../../common/constants';
19
+ import { useLinkPickerAnalytics, withInputFieldTracking, withLinkPickerAnalyticsContext } from '../../common/analytics';
20
+ import { normalizeUrl, isSafeUrl } from '../../common/utils/url';
19
21
  import { usePlugins } from '../../services/use-plugins';
20
22
  import { useSearchQuery } from '../../services/use-search-query';
21
- import { withLinkPickerAnalyticsContext } from '../../services/with-link-picker-analytics';
22
- import { messages } from './messages';
23
- import PanelTextInput from './text-input';
24
- import { normalizeUrl, isSafeUrl } from '../../common/utils/url';
25
23
  import useFixHeight from '../../controllers/use-fix-height';
24
+ import { messages } from './messages';
25
+ import TextInput from './text-input';
26
26
  import { rootContainerStyles, searchIconStyles, tabsWrapperStyles, flexColumnStyles, formFooterMargin } from './styled';
27
27
  import { browser } from './browser';
28
28
  import Announcer from './announcer';
@@ -62,6 +62,25 @@ function reducer(state, payload) {
62
62
 
63
63
  return _objectSpread(_objectSpread({}, state), payload);
64
64
  }
65
+ /**
66
+ * Bind input fields to analytics tracking
67
+ */
68
+
69
+
70
+ var getLinkFieldContent = function getLinkFieldContent(value) {
71
+ if (!Boolean(value)) {
72
+ return null;
73
+ }
74
+
75
+ return isSafeUrl(value) ? 'url' : 'text_string';
76
+ };
77
+
78
+ var LinkInputField = withInputFieldTracking(TextInput, 'link', function (event, attributes) {
79
+ return _objectSpread(_objectSpread({}, attributes), {}, {
80
+ linkFieldContent: getLinkFieldContent(event.currentTarget.value)
81
+ });
82
+ });
83
+ var DisplayTextInputField = withInputFieldTracking(TextInput, 'displayText');
65
84
 
66
85
  function LinkPicker(_ref) {
67
86
  var _errorFallback;
@@ -106,6 +125,11 @@ function LinkPicker(_ref) {
106
125
  var isEditing = !!initUrl;
107
126
  var selectedItem = items === null || items === void 0 ? void 0 : items[selectedIndex];
108
127
  var isSelectedItem = (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.url) === url;
128
+
129
+ var _useLinkPickerAnalyti = useLinkPickerAnalytics(),
130
+ trackAttribute = _useLinkPickerAnalyti.trackAttribute,
131
+ getAttributes = _useLinkPickerAnalyti.getAttributes;
132
+
109
133
  useEffect(function () {
110
134
  // Anything in here is fired on component mount.
111
135
  var event = createAnalyticsEvent(createEventPayload('ui.inlineDialog.viewed.linkPicker', {}));
@@ -121,14 +145,14 @@ function LinkPicker(_ref) {
121
145
  onContentResize();
122
146
  }
123
147
  }, [onContentResize, items, isLoading, isActivePlugin, tabs]);
124
- var handleChangeUrl = useCallback(function (url) {
148
+ var handleChangeUrl = useCallback(function (e) {
125
149
  dispatch({
126
- url: url
150
+ url: e.currentTarget.value
127
151
  });
128
152
  }, []);
129
- var handleChangeText = useCallback(function (displayText) {
153
+ var handleChangeText = useCallback(function (e) {
130
154
  dispatch({
131
- displayText: displayText
155
+ displayText: e.currentTarget.value
132
156
  });
133
157
  }, []);
134
158
  var handleKeyDown = useCallback(function (event) {
@@ -157,6 +181,11 @@ function LinkPicker(_ref) {
157
181
  }
158
182
 
159
183
  if ([KEY_CODE_ARROW_DOWN, KEY_CODE_ARROW_UP].includes(keyCode) && items[updatedIndex]) {
184
+ /**
185
+ * Manually track that the url has been updated using searchResult method
186
+ */
187
+ trackAttribute('linkFieldContent', getLinkFieldContent(items[updatedIndex].url));
188
+ trackAttribute('linkFieldContentInputMethod', 'searchResult');
160
189
  dispatch({
161
190
  activeIndex: updatedIndex,
162
191
  selectedIndex: updatedIndex,
@@ -164,7 +193,7 @@ function LinkPicker(_ref) {
164
193
  invalidUrl: false
165
194
  });
166
195
  }
167
- }, [activeIndex, items]);
196
+ }, [activeIndex, items, trackAttribute]);
168
197
  var handleClear = useCallback(function (field) {
169
198
  dispatch(_defineProperty({}, field, ''));
170
199
  }, []);
@@ -191,7 +220,12 @@ function LinkPicker(_ref) {
191
220
  var event = createAnalyticsEvent(createEventPayload('ui.form.submitted.linkPicker', {})); // Clone the event so that it can be emitted for consumer usage
192
221
  // This must happen BEFORE the original event is fired!
193
222
 
194
- var consumerEvent = event.clone(); // Dispatch the original event to our channel
223
+ var consumerEvent = event.clone(); // Cloned event doesnt have the attributes that are added by
224
+ // the analytics listener in the LinkPickerAnalyticsContext, add them here
225
+
226
+ consumerEvent === null || consumerEvent === void 0 ? void 0 : consumerEvent.update({
227
+ attributes: getAttributes()
228
+ }); // Dispatch the original event to our channel
195
229
 
196
230
  event.fire(ANALYTICS_CHANNEL);
197
231
  onSubmit(_objectSpread({
@@ -204,7 +238,7 @@ function LinkPicker(_ref) {
204
238
  }, inputType === 'manual' ? {
205
239
  rawUrl: state.url
206
240
  } : {}), consumerEvent);
207
- }, [displayText, onSubmit, state.url, createAnalyticsEvent]);
241
+ }, [displayText, onSubmit, state.url, createAnalyticsEvent, getAttributes]);
208
242
  var handleSelected = useCallback(function (objectId) {
209
243
  var selectedItem = items === null || items === void 0 ? void 0 : items.find(function (item) {
210
244
  return item.objectId === objectId;
@@ -214,9 +248,15 @@ function LinkPicker(_ref) {
214
248
  var _url = selectedItem.url,
215
249
  name = selectedItem.name;
216
250
  dispatchEvent(new Event('submit'));
251
+ /**
252
+ * Manually track that the url has been updated using searchResult method
253
+ */
254
+
255
+ trackAttribute('linkFieldContent', getLinkFieldContent(_url));
256
+ trackAttribute('linkFieldContentInputMethod', 'searchResult');
217
257
  handleInsert(_url, name, 'typeAhead');
218
258
  }
219
- }, [handleInsert, items]);
259
+ }, [handleInsert, trackAttribute, items]);
220
260
  var handleSubmit = useCallback(function (event) {
221
261
  event.preventDefault();
222
262
 
@@ -260,7 +300,7 @@ function LinkPicker(_ref) {
260
300
  delay: 250
261
301
  }), jsx(VisuallyHidden, {
262
302
  id: screenReaderDescriptionId
263
- }, jsx(FormattedMessage, messages.searchLinkAriaDescription)), jsx(PanelTextInput, {
303
+ }, jsx(FormattedMessage, messages.searchLinkAriaDescription)), jsx(LinkInputField, {
264
304
  role: "combobox",
265
305
  autoComplete: "off",
266
306
  name: "url",
@@ -280,7 +320,7 @@ function LinkPicker(_ref) {
280
320
  onClear: handleClear,
281
321
  onKeyDown: handleKeyDown,
282
322
  onChange: handleChangeUrl
283
- }), jsx(PanelTextInput, {
323
+ }), jsx(DisplayTextInputField, {
284
324
  autoComplete: "off",
285
325
  name: "displayText",
286
326
  testId: testIds.textInputField,
@@ -4,6 +4,8 @@ import _extends from "@babel/runtime/helpers/extends";
4
4
  import { jsx } from '@emotion/react';
5
5
  import { useIntl, FormattedMessage, defineMessages } from 'react-intl-next';
6
6
  import Button from '@atlaskit/button';
7
+ import EmptyState from '@atlaskit/empty-state';
8
+ import GenericErrorSVG from '../../../common/generic-error-svg';
7
9
  export var CONTACT_SUPPORT_LINK = 'https://support.atlassian.com/contact/';
8
10
  export var messages = defineMessages({
9
11
  searchErrorHeader: {
@@ -17,8 +19,6 @@ export var messages = defineMessages({
17
19
  description: 'Describes possible actions when search throws an error'
18
20
  }
19
21
  });
20
- import EmptyState from '@atlaskit/empty-state';
21
- import GenericErrorSVG from '../../../common/generic-error-svg';
22
22
  export var testIds = {
23
23
  searchError: 'link-search-error'
24
24
  };
@@ -1,6 +1,6 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
2
  import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
3
- var _excluded = ["name", "label", "autoFocus", "onRedo", "onUndo", "onChange", "onKeyDown", "onClear", "clearLabel", "error"];
3
+ var _excluded = ["name", "label", "autoFocus", "onRedo", "onUndo", "onKeyDown", "onClear", "clearLabel", "error"];
4
4
 
5
5
  /** @jsx jsx */
6
6
  import { useCallback, useRef, Fragment } from 'react';
@@ -10,21 +10,20 @@ import Tooltip from '@atlaskit/tooltip';
10
10
  import Textfield from '@atlaskit/textfield';
11
11
  import Selectclear from '@atlaskit/icon/glyph/select-clear';
12
12
  import { isRedoEvent, isUndoEvent } from '../utils';
13
- import { clearTextButtonStyles, fieldStyles } from './styled';
14
13
  import { testIds } from '../';
14
+ import { clearTextButtonStyles, fieldStyles } from './styled';
15
15
 
16
- var PanelTextInput = function PanelTextInput(props) {
17
- var name = props.name,
18
- label = props.label,
19
- autoFocus = props.autoFocus,
20
- onRedo = props.onRedo,
21
- onUndo = props.onUndo,
22
- onChange = props.onChange,
23
- onKeyDown = props.onKeyDown,
24
- onClear = props.onClear,
25
- clearLabel = props.clearLabel,
26
- error = props.error,
27
- restProps = _objectWithoutProperties(props, _excluded);
16
+ var TextInput = function TextInput(_ref) {
17
+ var name = _ref.name,
18
+ label = _ref.label,
19
+ autoFocus = _ref.autoFocus,
20
+ onRedo = _ref.onRedo,
21
+ onUndo = _ref.onUndo,
22
+ onKeyDown = _ref.onKeyDown,
23
+ onClear = _ref.onClear,
24
+ clearLabel = _ref.clearLabel,
25
+ error = _ref.error,
26
+ restProps = _objectWithoutProperties(_ref, _excluded);
28
27
 
29
28
  var inputRef = useRef(null);
30
29
  var handleRef = useCallback(function (input) {
@@ -39,9 +38,6 @@ var PanelTextInput = function PanelTextInput(props) {
39
38
  }
40
39
  }
41
40
  }, [autoFocus]);
42
- var handleChange = useCallback(function (e) {
43
- onChange(e.currentTarget.value);
44
- }, [onChange]);
45
41
  var handleKeydown = useCallback(function (e) {
46
42
  if (typeof onUndo === 'function' && isUndoEvent(e)) {
47
43
  e.preventDefault();
@@ -79,11 +75,10 @@ var PanelTextInput = function PanelTextInput(props) {
79
75
  }, jsx(Field, {
80
76
  label: label,
81
77
  name: name
82
- }, function (_ref) {
83
- var fieldProps = _ref.fieldProps;
78
+ }, function (_ref2) {
79
+ var fieldProps = _ref2.fieldProps;
84
80
  return jsx(Fragment, null, jsx(Textfield, _extends({}, fieldProps, restProps, {
85
81
  onKeyDown: handleKeydown,
86
- onChange: handleChange,
87
82
  ref: handleRef,
88
83
  elemAfterInput: clearText,
89
84
  isInvalid: !!error
@@ -93,4 +88,4 @@ var PanelTextInput = function PanelTextInput(props) {
93
88
  }));
94
89
  };
95
90
 
96
- export default PanelTextInput;
91
+ export default TextInput;
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/link-picker",
3
- "version": "1.9.2",
3
+ "version": "1.9.4",
4
4
  "sideEffects": false
5
5
  }
@@ -3,19 +3,39 @@
3
3
  *
4
4
  * Generates analytics utilities from the package analytics spec yaml
5
5
  *
6
- * @codegen <<SignedSource::115bafba068f36f76da67200ee2d08c4>>
6
+ * @codegen <<SignedSource::3c46f1d8f463b6898a7a0e1e1f4676b4>>
7
7
  * @codegenCommand yarn workspace @atlaskit/link-picker run codegen-analytics
8
8
  */
9
+ export declare type PackageMetaDataType = {
10
+ packageName: '@atlaskit/link-picker';
11
+ packageVersion: string;
12
+ source: 'linkPicker';
13
+ componentName: 'linkPicker';
14
+ };
9
15
  export declare type LinkPickerAnalyticsContextType = {
10
16
  linkState: 'editLink' | 'newLink';
17
+ linkFieldContent: 'url' | 'text_string' | null;
18
+ linkFieldContentInputMethod: 'manual' | 'paste' | 'searchResult' | null;
19
+ displayTextFieldContent: 'text_string' | null;
20
+ displayTextFieldContentInputMethod: 'manual' | 'paste' | null;
11
21
  };
12
22
  export declare type FormSubmittedLinkPickerAttributesType = {};
13
23
  export declare type InlineDialogViewedLinkPickerAttributesType = {};
14
24
  export declare type InlineDialogClosedLinkPickerAttributesType = {};
25
+ export declare type TextFieldUpdatedLinkFieldAttributesType = {};
26
+ export declare type TextFieldUpdatedDisplayTextFieldAttributesType = {};
27
+ export declare type LinkPickerUnhandledErrorCaughtAttributesType = {
28
+ browserInfo: string;
29
+ error: string;
30
+ componentStack: string;
31
+ };
15
32
  declare type AnalyticsEventAttributes = {
16
33
  'ui.form.submitted.linkPicker': FormSubmittedLinkPickerAttributesType;
17
34
  'ui.inlineDialog.viewed.linkPicker': InlineDialogViewedLinkPickerAttributesType;
18
35
  'ui.inlineDialog.closed.linkPicker': InlineDialogClosedLinkPickerAttributesType;
36
+ 'ui.textField.updated.linkField': TextFieldUpdatedLinkFieldAttributesType;
37
+ 'ui.textField.updated.displayTextField': TextFieldUpdatedDisplayTextFieldAttributesType;
38
+ 'ui.linkPicker.unhandledErrorCaught': LinkPickerUnhandledErrorCaughtAttributesType;
19
39
  };
20
40
  declare function createEventPayload<K extends keyof AnalyticsEventAttributes>(eventKey: K, attributes: AnalyticsEventAttributes[K]): {
21
41
  eventType: string;
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ import { LinkPickerAnalyticsContextType } from '../../analytics.codegen';
3
+ export declare type TrackAttribute = <K extends keyof LinkPickerAnalyticsContextType>(attribute: K, value: LinkPickerAnalyticsContextType[K]) => void;
4
+ interface AnalyticsContextType {
5
+ /**
6
+ * Update a single attribute in the analytics context
7
+ */
8
+ trackAttribute: TrackAttribute;
9
+ /**
10
+ * Update a set of attributes in the analytics context
11
+ */
12
+ trackAttributes: (attributes: Partial<LinkPickerAnalyticsContextType>) => void;
13
+ /**
14
+ * Retrieve the current set of attributes
15
+ * Can be used by components to derive/compute new attributes for specific events
16
+ */
17
+ getAttributes: () => LinkPickerAnalyticsContextType;
18
+ }
19
+ /**
20
+ * Hook that exposes the context-level attribute getters and setters.
21
+ */
22
+ export declare const useLinkPickerAnalytics: () => AnalyticsContextType;
23
+ /**
24
+ * Wraps a component with the analytics context + listener to update events with contextual-level attributes.
25
+ * Should be implemented once at the root of the link picker.
26
+ */
27
+ export declare function withLinkPickerAnalyticsContext<P>(WrappedComponent: React.ComponentType<P>): React.ComponentType<P>;
28
+ export {};
@@ -0,0 +1,2 @@
1
+ export { useLinkPickerAnalytics, withLinkPickerAnalyticsContext, } from './context';
2
+ export { withInputFieldTracking } from './input-field-tracking';