@atlaskit/link-picker 1.9.2 → 1.9.3

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 (42) hide show
  1. package/CHANGELOG.md +6 -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/index.js +2 -1
  8. package/dist/cjs/ui/link-picker/index.js +56 -16
  9. package/dist/cjs/ui/link-picker/text-input/index.js +18 -23
  10. package/dist/cjs/version.json +1 -1
  11. package/dist/es2019/analytics.codegen.js +1 -1
  12. package/dist/es2019/common/analytics/context.js +95 -0
  13. package/dist/es2019/common/analytics/index.js +2 -0
  14. package/dist/es2019/common/analytics/input-field-tracking.js +85 -0
  15. package/dist/es2019/common/constants.js +1 -1
  16. package/dist/es2019/ui/index.js +2 -1
  17. package/dist/es2019/ui/link-picker/index.js +51 -14
  18. package/dist/es2019/ui/link-picker/link-search-error/index.js +2 -2
  19. package/dist/es2019/ui/link-picker/text-input/index.js +14 -20
  20. package/dist/es2019/version.json +1 -1
  21. package/dist/esm/analytics.codegen.js +1 -1
  22. package/dist/esm/common/analytics/context.js +110 -0
  23. package/dist/esm/common/analytics/index.js +2 -0
  24. package/dist/esm/common/analytics/input-field-tracking.js +91 -0
  25. package/dist/esm/common/constants.js +1 -1
  26. package/dist/esm/ui/index.js +2 -1
  27. package/dist/esm/ui/link-picker/index.js +54 -14
  28. package/dist/esm/ui/link-picker/link-search-error/index.js +2 -2
  29. package/dist/esm/ui/link-picker/text-input/index.js +16 -21
  30. package/dist/esm/version.json +1 -1
  31. package/dist/types/analytics.codegen.d.ts +9 -1
  32. package/dist/types/common/analytics/context.d.ts +28 -0
  33. package/dist/types/common/analytics/index.d.ts +2 -0
  34. package/dist/types/common/analytics/input-field-tracking.d.ts +17 -0
  35. package/dist/types/common/constants.d.ts +1 -1
  36. package/dist/types/ui/index.d.ts +1 -0
  37. package/dist/types/ui/link-picker/text-input/index.d.ts +6 -8
  38. package/package.json +1 -1
  39. package/dist/cjs/services/with-link-picker-analytics/index.js +0 -33
  40. package/dist/es2019/services/with-link-picker-analytics/index.js +0 -15
  41. package/dist/esm/services/with-link-picker-analytics/index.js +0 -17
  42. package/dist/types/services/with-link-picker-analytics/index.d.ts +0 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @atlaskit/link-picker
2
2
 
3
+ ## 1.9.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`e3f8fb1f348`](https://bitbucket.org/atlassian/atlassian-frontend/commits/e3f8fb1f348) - Adds tracking of how the input fields are populated ie paste vs manual vs search.
8
+
3
9
  ## 1.9.2
4
10
 
5
11
  ### Patch Changes
@@ -10,7 +10,7 @@ exports.default = void 0;
10
10
  *
11
11
  * Generates analytics utilities from the package analytics spec yaml
12
12
  *
13
- * @codegen <<SignedSource::115bafba068f36f76da67200ee2d08c4>>
13
+ * @codegen <<SignedSource::e4f7029050ef6edde92cac3d9535cd4a>>
14
14
  * @codegenCommand yarn workspace @atlaskit/link-picker run codegen-analytics
15
15
  */
16
16
  function createEventPayload(eventKey, attributes) {
@@ -0,0 +1,137 @@
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.useLinkPickerAnalytics = void 0;
11
+ exports.withLinkPickerAnalyticsContext = withLinkPickerAnalyticsContext;
12
+
13
+ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
14
+
15
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
16
+
17
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
18
+
19
+ var _react = _interopRequireWildcard(require("react"));
20
+
21
+ var _analyticsNext = require("@atlaskit/analytics-next");
22
+
23
+ var _constants = require("../constants");
24
+
25
+ var _url = require("../utils/url");
26
+
27
+ 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); }
28
+
29
+ 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; }
30
+
31
+ 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; }
32
+
33
+ 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) { (0, _defineProperty2.default)(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; }
34
+
35
+ var DEFAULT_CONTEXT_ATTRIBUTES = {
36
+ linkState: 'newLink',
37
+ linkFieldContent: null,
38
+ linkFieldContentInputMethod: null,
39
+ displayTextFieldContent: null,
40
+ displayTextFieldContentInputMethod: null
41
+ };
42
+
43
+ var LinkPickerAnalyticsContext = /*#__PURE__*/_react.default.createContext({
44
+ trackAttribute: function trackAttribute() {},
45
+ trackAttributes: function trackAttributes() {},
46
+ getAttributes: function getAttributes() {
47
+ return DEFAULT_CONTEXT_ATTRIBUTES;
48
+ }
49
+ });
50
+ /**
51
+ * TODO: Should we hook this into the form state/reducer?
52
+ */
53
+
54
+
55
+ var contextAttributesFromIntialProps = function contextAttributesFromIntialProps(props) {
56
+ return {
57
+ linkState: (0, _url.normalizeUrl)(props.url) ? 'editLink' : 'newLink',
58
+ linkFieldContent: (0, _url.normalizeUrl)(props.url) ? 'url' : null,
59
+ displayTextFieldContent: Boolean(props.displayText) ? 'text_string' : null
60
+ };
61
+ };
62
+ /**
63
+ * Context for tracking attributes to deliver at a context-level
64
+ */
65
+
66
+
67
+ var LinkPickerAnalytics = function LinkPickerAnalytics(_ref) {
68
+ var initialContext = _ref.initialContext,
69
+ children = _ref.children;
70
+ var dataRef = (0, _react.useRef)(_objectSpread(_objectSpread({}, DEFAULT_CONTEXT_ATTRIBUTES), initialContext));
71
+ var methods = (0, _react.useMemo)(function () {
72
+ return {
73
+ trackAttribute: function trackAttribute(key, value) {
74
+ dataRef.current[key] = value;
75
+ },
76
+ trackAttributes: function trackAttributes(attributes) {
77
+ dataRef.current = _objectSpread(_objectSpread({}, dataRef.current), attributes);
78
+ },
79
+ getAttributes: function getAttributes() {
80
+ return dataRef.current;
81
+ }
82
+ };
83
+ }, []);
84
+ return /*#__PURE__*/_react.default.createElement(LinkPickerAnalyticsContext.Provider, {
85
+ value: methods
86
+ }, children);
87
+ };
88
+ /**
89
+ * Hook that exposes the context-level attribute getters and setters.
90
+ */
91
+
92
+
93
+ var useLinkPickerAnalytics = function useLinkPickerAnalytics() {
94
+ return (0, _react.useContext)(LinkPickerAnalyticsContext);
95
+ };
96
+ /**
97
+ * Wrap component in "attributes" context store and initialise the initial context attributes from props.
98
+ */
99
+
100
+
101
+ exports.useLinkPickerAnalytics = useLinkPickerAnalytics;
102
+
103
+ function withLinkPickerAnalytics(WrappedComponent) {
104
+ return function (props) {
105
+ var _useState = (0, _react.useState)(function () {
106
+ return contextAttributesFromIntialProps(props);
107
+ }),
108
+ _useState2 = (0, _slicedToArray2.default)(_useState, 1),
109
+ initialContext = _useState2[0];
110
+
111
+ return /*#__PURE__*/_react.default.createElement(LinkPickerAnalytics, (0, _extends2.default)({
112
+ initialContext: initialContext
113
+ }, props), /*#__PURE__*/_react.default.createElement(WrappedComponent, props));
114
+ };
115
+ }
116
+ /**
117
+ * Wraps a component with the analytics context + listener to update events with contextual-level attributes.
118
+ * Should be implemented once at the root of the link picker.
119
+ */
120
+
121
+
122
+ function withLinkPickerAnalyticsContext(WrappedComponent) {
123
+ return withLinkPickerAnalytics(function (props) {
124
+ var _useLinkPickerAnalyti = useLinkPickerAnalytics(),
125
+ getAttributes = _useLinkPickerAnalyti.getAttributes;
126
+
127
+ var onEvent = (0, _react.useCallback)(function (event) {
128
+ event.update({
129
+ attributes: getAttributes()
130
+ });
131
+ }, [getAttributes]);
132
+ return /*#__PURE__*/_react.default.createElement(_analyticsNext.AnalyticsListener, {
133
+ channel: _constants.ANALYTICS_CHANNEL,
134
+ onEvent: onEvent
135
+ }, /*#__PURE__*/_react.default.createElement(WrappedComponent, props));
136
+ });
137
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "useLinkPickerAnalytics", {
7
+ enumerable: true,
8
+ get: function get() {
9
+ return _context.useLinkPickerAnalytics;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "withInputFieldTracking", {
13
+ enumerable: true,
14
+ get: function get() {
15
+ return _inputFieldTracking.withInputFieldTracking;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "withLinkPickerAnalyticsContext", {
19
+ enumerable: true,
20
+ get: function get() {
21
+ return _context.withLinkPickerAnalyticsContext;
22
+ }
23
+ });
24
+
25
+ var _context = require("./context");
26
+
27
+ var _inputFieldTracking = require("./input-field-tracking");
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ var _typeof3 = require("@babel/runtime/helpers/typeof");
6
+
7
+ Object.defineProperty(exports, "__esModule", {
8
+ value: true
9
+ });
10
+ exports.withInputFieldTracking = void 0;
11
+
12
+ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
13
+
14
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
15
+
16
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
17
+
18
+ var _react = _interopRequireWildcard(require("react"));
19
+
20
+ var _analyticsNext = require("@atlaskit/analytics-next");
21
+
22
+ var _constants = require("../constants");
23
+
24
+ var _analytics = _interopRequireDefault(require("../../analytics.codegen"));
25
+
26
+ var _2 = require(".");
27
+
28
+ 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); }
29
+
30
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof3(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; }
31
+
32
+ var isEventWithInputType = function isEventWithInputType(e) {
33
+ return (0, _typeof2.default)(e) === 'object' && e !== null && 'inputType' in e;
34
+ };
35
+
36
+ var getChangeEventInputMethodType = function getChangeEventInputMethodType(event) {
37
+ if (isEventWithInputType(event.nativeEvent) && typeof event.nativeEvent.inputType === 'string' && event.nativeEvent.inputType.includes('insertFromPaste')) {
38
+ return 'paste';
39
+ }
40
+
41
+ return 'manual';
42
+ };
43
+ /**
44
+ * Wraps an input field and connects it to the link picker analytics context to provide a baseline of analytics tracking
45
+ * @param WrappedComponent The input field to be tracked
46
+ * @param field The name of the field (ie matching actionSubjectId)
47
+ * @param filterOnChangeEventAttrs Function to hook into and filter/customise the default attribute tracking logic
48
+ * @returns HOC Input field with tracking
49
+ */
50
+
51
+
52
+ var withInputFieldTracking = function withInputFieldTracking(WrappedComponent, field) {
53
+ var filterOnChangeEventAttrs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function (_, attributes) {
54
+ return attributes;
55
+ };
56
+ return function (props) {
57
+ var onBlur = props.onBlur,
58
+ onChange = props.onChange,
59
+ onClear = props.onClear,
60
+ onFocus = props.onFocus;
61
+
62
+ var _useLinkPickerAnalyti = (0, _2.useLinkPickerAnalytics)(),
63
+ trackAttribute = _useLinkPickerAnalyti.trackAttribute,
64
+ trackAttributes = _useLinkPickerAnalyti.trackAttributes;
65
+
66
+ var _useAnalyticsEvents = (0, _analyticsNext.useAnalyticsEvents)(),
67
+ createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent; // Ref to track if the value of the field has changed between focus and blur
68
+
69
+
70
+ var valueOnFocus = (0, _react.useRef)(null); // Track the content and input method on every direct change to the input
71
+
72
+ var handleOnChange = (0, _react.useCallback)(function (event) {
73
+ var _filterOnChangeEventA;
74
+
75
+ // TODO: See above re template literal, improve typesafety here
76
+ // trackAttribute(`${field}FieldContent`, contentTypeFn(event.currentTarget.value));
77
+ // trackAttribute(`${field}FieldContentInputMethod`, methodTypeFn(event));
78
+ trackAttributes(filterOnChangeEventAttrs(event, (_filterOnChangeEventA = {}, (0, _defineProperty2.default)(_filterOnChangeEventA, "".concat(field, "FieldContent"), Boolean(event.currentTarget.value) ? 'text_string' : null), (0, _defineProperty2.default)(_filterOnChangeEventA, "".concat(field, "FieldContentInputMethod"), getChangeEventInputMethodType(event)), _filterOnChangeEventA)));
79
+ onChange === null || onChange === void 0 ? void 0 : onChange(event);
80
+ }, [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
81
+
82
+ var handleOnFocus = (0, _react.useCallback)(function (event) {
83
+ valueOnFocus.current = event.currentTarget.value;
84
+ onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
85
+ }, [onFocus]); // Fire updated events only when the user's focus leaves the field
86
+
87
+ var handleOnBlur = (0, _react.useCallback)(function (event) {
88
+ // Only fire if the value has changed since the field was focused (we are only concerned with the net change to the field)
89
+ if (event.currentTarget.value !== valueOnFocus.current) {
90
+ createAnalyticsEvent((0, _analytics.default)("ui.textField.updated.".concat(field, "Field"), {})).fire(_constants.ANALYTICS_CHANNEL);
91
+ }
92
+
93
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
94
+ }, [onBlur, createAnalyticsEvent]); // Track the values as null when the text field is cleared
95
+
96
+ var handleOnClear = (0, _react.useCallback)(function () {
97
+ trackAttribute("".concat(field, "FieldContent"), null);
98
+ trackAttribute("".concat(field, "FieldContentInputMethod"), null);
99
+
100
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
101
+ args[_key] = arguments[_key];
102
+ }
103
+
104
+ onClear === null || onClear === void 0 ? void 0 : onClear.apply(void 0, args);
105
+ }, [onClear, trackAttribute]);
106
+ return /*#__PURE__*/_react.default.createElement(WrappedComponent, (0, _extends2.default)({}, props, {
107
+ onBlur: handleOnBlur,
108
+ onChange: handleOnChange,
109
+ onClear: handleOnClear,
110
+ onFocus: handleOnFocus
111
+ }));
112
+ };
113
+ };
114
+
115
+ exports.withInputFieldTracking = withInputFieldTracking;
@@ -6,5 +6,5 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.COMPONENT_NAME = exports.ANALYTICS_CHANNEL = void 0;
7
7
  var ANALYTICS_CHANNEL = 'media';
8
8
  exports.ANALYTICS_CHANNEL = ANALYTICS_CHANNEL;
9
- var COMPONENT_NAME = 'LinkPicker';
9
+ var COMPONENT_NAME = 'linkPicker';
10
10
  exports.COMPONENT_NAME = COMPONENT_NAME;
@@ -30,7 +30,8 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
30
30
  var PACKAGE_DATA = {
31
31
  packageName: _version.name,
32
32
  packageVersion: _version.version,
33
- componentName: _constants.COMPONENT_NAME
33
+ componentName: _constants.COMPONENT_NAME,
34
+ source: _constants.COMPONENT_NAME
34
35
  };
35
36
  exports.PACKAGE_DATA = PACKAGE_DATA;
36
37
  var LazyLinkPicker = (0, _reactLooselyLazy.lazyForPaint)(function () {
@@ -33,20 +33,20 @@ var _analytics = _interopRequireDefault(require("../../analytics.codegen"));
33
33
 
34
34
  var _constants = require("../../common/constants");
35
35
 
36
+ var _analytics2 = require("../../common/analytics");
37
+
38
+ var _url2 = require("../../common/utils/url");
39
+
36
40
  var _usePlugins2 = require("../../services/use-plugins");
37
41
 
38
42
  var _useSearchQuery = require("../../services/use-search-query");
39
43
 
40
- var _withLinkPickerAnalytics = require("../../services/with-link-picker-analytics");
44
+ var _useFixHeight = _interopRequireDefault(require("../../controllers/use-fix-height"));
41
45
 
42
46
  var _messages = require("./messages");
43
47
 
44
48
  var _textInput = _interopRequireDefault(require("./text-input"));
45
49
 
46
- var _url2 = require("../../common/utils/url");
47
-
48
- var _useFixHeight = _interopRequireDefault(require("../../controllers/use-fix-height"));
49
-
50
50
  var _styled = require("./styled");
51
51
 
52
52
  var _browser = require("./browser");
@@ -106,6 +106,25 @@ function reducer(state, payload) {
106
106
 
107
107
  return _objectSpread(_objectSpread({}, state), payload);
108
108
  }
109
+ /**
110
+ * Bind input fields to analytics tracking
111
+ */
112
+
113
+
114
+ var getLinkFieldContent = function getLinkFieldContent(value) {
115
+ if (!Boolean(value)) {
116
+ return null;
117
+ }
118
+
119
+ return (0, _url2.isSafeUrl)(value) ? 'url' : 'text_string';
120
+ };
121
+
122
+ var LinkInputField = (0, _analytics2.withInputFieldTracking)(_textInput.default, 'link', function (event, attributes) {
123
+ return _objectSpread(_objectSpread({}, attributes), {}, {
124
+ linkFieldContent: getLinkFieldContent(event.currentTarget.value)
125
+ });
126
+ });
127
+ var DisplayTextInputField = (0, _analytics2.withInputFieldTracking)(_textInput.default, 'displayText');
109
128
 
110
129
  function LinkPicker(_ref) {
111
130
  var _errorFallback;
@@ -150,6 +169,11 @@ function LinkPicker(_ref) {
150
169
  var isEditing = !!initUrl;
151
170
  var selectedItem = items === null || items === void 0 ? void 0 : items[selectedIndex];
152
171
  var isSelectedItem = (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.url) === url;
172
+
173
+ var _useLinkPickerAnalyti = (0, _analytics2.useLinkPickerAnalytics)(),
174
+ trackAttribute = _useLinkPickerAnalyti.trackAttribute,
175
+ getAttributes = _useLinkPickerAnalyti.getAttributes;
176
+
153
177
  (0, _react.useEffect)(function () {
154
178
  // Anything in here is fired on component mount.
155
179
  var event = createAnalyticsEvent((0, _analytics.default)('ui.inlineDialog.viewed.linkPicker', {}));
@@ -165,14 +189,14 @@ function LinkPicker(_ref) {
165
189
  onContentResize();
166
190
  }
167
191
  }, [onContentResize, items, isLoading, isActivePlugin, tabs]);
168
- var handleChangeUrl = (0, _react.useCallback)(function (url) {
192
+ var handleChangeUrl = (0, _react.useCallback)(function (e) {
169
193
  dispatch({
170
- url: url
194
+ url: e.currentTarget.value
171
195
  });
172
196
  }, []);
173
- var handleChangeText = (0, _react.useCallback)(function (displayText) {
197
+ var handleChangeText = (0, _react.useCallback)(function (e) {
174
198
  dispatch({
175
- displayText: displayText
199
+ displayText: e.currentTarget.value
176
200
  });
177
201
  }, []);
178
202
  var handleKeyDown = (0, _react.useCallback)(function (event) {
@@ -201,6 +225,11 @@ function LinkPicker(_ref) {
201
225
  }
202
226
 
203
227
  if ([KEY_CODE_ARROW_DOWN, KEY_CODE_ARROW_UP].includes(keyCode) && items[updatedIndex]) {
228
+ /**
229
+ * Manually track that the url has been updated using searchResult method
230
+ */
231
+ trackAttribute('linkFieldContent', getLinkFieldContent(items[updatedIndex].url));
232
+ trackAttribute('linkFieldContentInputMethod', 'searchResult');
204
233
  dispatch({
205
234
  activeIndex: updatedIndex,
206
235
  selectedIndex: updatedIndex,
@@ -208,7 +237,7 @@ function LinkPicker(_ref) {
208
237
  invalidUrl: false
209
238
  });
210
239
  }
211
- }, [activeIndex, items]);
240
+ }, [activeIndex, items, trackAttribute]);
212
241
  var handleClear = (0, _react.useCallback)(function (field) {
213
242
  dispatch((0, _defineProperty2.default)({}, field, ''));
214
243
  }, []);
@@ -235,7 +264,12 @@ function LinkPicker(_ref) {
235
264
  var event = createAnalyticsEvent((0, _analytics.default)('ui.form.submitted.linkPicker', {})); // Clone the event so that it can be emitted for consumer usage
236
265
  // This must happen BEFORE the original event is fired!
237
266
 
238
- var consumerEvent = event.clone(); // Dispatch the original event to our channel
267
+ var consumerEvent = event.clone(); // Cloned event doesnt have the attributes that are added by
268
+ // the analytics listener in the LinkPickerAnalyticsContext, add them here
269
+
270
+ consumerEvent === null || consumerEvent === void 0 ? void 0 : consumerEvent.update({
271
+ attributes: getAttributes()
272
+ }); // Dispatch the original event to our channel
239
273
 
240
274
  event.fire(_constants.ANALYTICS_CHANNEL);
241
275
  onSubmit(_objectSpread({
@@ -248,7 +282,7 @@ function LinkPicker(_ref) {
248
282
  }, inputType === 'manual' ? {
249
283
  rawUrl: state.url
250
284
  } : {}), consumerEvent);
251
- }, [displayText, onSubmit, state.url, createAnalyticsEvent]);
285
+ }, [displayText, onSubmit, state.url, createAnalyticsEvent, getAttributes]);
252
286
  var handleSelected = (0, _react.useCallback)(function (objectId) {
253
287
  var selectedItem = items === null || items === void 0 ? void 0 : items.find(function (item) {
254
288
  return item.objectId === objectId;
@@ -258,9 +292,15 @@ function LinkPicker(_ref) {
258
292
  var _url = selectedItem.url,
259
293
  name = selectedItem.name;
260
294
  dispatchEvent(new Event('submit'));
295
+ /**
296
+ * Manually track that the url has been updated using searchResult method
297
+ */
298
+
299
+ trackAttribute('linkFieldContent', getLinkFieldContent(_url));
300
+ trackAttribute('linkFieldContentInputMethod', 'searchResult');
261
301
  handleInsert(_url, name, 'typeAhead');
262
302
  }
263
- }, [handleInsert, items]);
303
+ }, [handleInsert, trackAttribute, items]);
264
304
  var handleSubmit = (0, _react.useCallback)(function (event) {
265
305
  event.preventDefault();
266
306
 
@@ -304,7 +344,7 @@ function LinkPicker(_ref) {
304
344
  delay: 250
305
345
  }), (0, _react2.jsx)(_visuallyHidden.default, {
306
346
  id: screenReaderDescriptionId
307
- }, (0, _react2.jsx)(_reactIntlNext.FormattedMessage, _messages.messages.searchLinkAriaDescription)), (0, _react2.jsx)(_textInput.default, {
347
+ }, (0, _react2.jsx)(_reactIntlNext.FormattedMessage, _messages.messages.searchLinkAriaDescription)), (0, _react2.jsx)(LinkInputField, {
308
348
  role: "combobox",
309
349
  autoComplete: "off",
310
350
  name: "url",
@@ -324,7 +364,7 @@ function LinkPicker(_ref) {
324
364
  onClear: handleClear,
325
365
  onKeyDown: handleKeyDown,
326
366
  onChange: handleChangeUrl
327
- }), (0, _react2.jsx)(_textInput.default, {
367
+ }), (0, _react2.jsx)(DisplayTextInputField, {
328
368
  autoComplete: "off",
329
369
  name: "displayText",
330
370
  testId: testIds.textInputField,
@@ -375,7 +415,7 @@ function LinkPicker(_ref) {
375
415
  }));
376
416
  }
377
417
 
378
- var _default = (0, _withLinkPickerAnalytics.withLinkPickerAnalyticsContext)( /*#__PURE__*/(0, _react.memo)(LinkPicker));
418
+ var _default = (0, _analytics2.withLinkPickerAnalyticsContext)( /*#__PURE__*/(0, _react.memo)(LinkPicker));
379
419
 
380
420
  exports.default = _default;
381
421
 
@@ -25,24 +25,23 @@ var _selectClear = _interopRequireDefault(require("@atlaskit/icon/glyph/select-c
25
25
 
26
26
  var _utils = require("../utils");
27
27
 
28
- var _styled = require("./styled");
29
-
30
28
  var _ = require("../");
31
29
 
32
- var _excluded = ["name", "label", "autoFocus", "onRedo", "onUndo", "onChange", "onKeyDown", "onClear", "clearLabel", "error"];
33
-
34
- var PanelTextInput = function PanelTextInput(props) {
35
- var name = props.name,
36
- label = props.label,
37
- autoFocus = props.autoFocus,
38
- onRedo = props.onRedo,
39
- onUndo = props.onUndo,
40
- onChange = props.onChange,
41
- onKeyDown = props.onKeyDown,
42
- onClear = props.onClear,
43
- clearLabel = props.clearLabel,
44
- error = props.error,
45
- restProps = (0, _objectWithoutProperties2.default)(props, _excluded);
30
+ var _styled = require("./styled");
31
+
32
+ var _excluded = ["name", "label", "autoFocus", "onRedo", "onUndo", "onKeyDown", "onClear", "clearLabel", "error"];
33
+
34
+ var TextInput = function TextInput(_ref) {
35
+ var name = _ref.name,
36
+ label = _ref.label,
37
+ autoFocus = _ref.autoFocus,
38
+ onRedo = _ref.onRedo,
39
+ onUndo = _ref.onUndo,
40
+ onKeyDown = _ref.onKeyDown,
41
+ onClear = _ref.onClear,
42
+ clearLabel = _ref.clearLabel,
43
+ error = _ref.error,
44
+ restProps = (0, _objectWithoutProperties2.default)(_ref, _excluded);
46
45
  var inputRef = (0, _react.useRef)(null);
47
46
  var handleRef = (0, _react.useCallback)(function (input) {
48
47
  if (input) {
@@ -56,9 +55,6 @@ var PanelTextInput = function PanelTextInput(props) {
56
55
  }
57
56
  }
58
57
  }, [autoFocus]);
59
- var handleChange = (0, _react.useCallback)(function (e) {
60
- onChange(e.currentTarget.value);
61
- }, [onChange]);
62
58
  var handleKeydown = (0, _react.useCallback)(function (e) {
63
59
  if (typeof onUndo === 'function' && (0, _utils.isUndoEvent)(e)) {
64
60
  e.preventDefault();
@@ -96,11 +92,10 @@ var PanelTextInput = function PanelTextInput(props) {
96
92
  }, (0, _react2.jsx)(_form.Field, {
97
93
  label: label,
98
94
  name: name
99
- }, function (_ref) {
100
- var fieldProps = _ref.fieldProps;
95
+ }, function (_ref2) {
96
+ var fieldProps = _ref2.fieldProps;
101
97
  return (0, _react2.jsx)(_react.Fragment, null, (0, _react2.jsx)(_textfield.default, (0, _extends2.default)({}, fieldProps, restProps, {
102
98
  onKeyDown: handleKeydown,
103
- onChange: handleChange,
104
99
  ref: handleRef,
105
100
  elemAfterInput: clearText,
106
101
  isInvalid: !!error
@@ -110,5 +105,5 @@ var PanelTextInput = function PanelTextInput(props) {
110
105
  }));
111
106
  };
112
107
 
113
- var _default = PanelTextInput;
108
+ var _default = TextInput;
114
109
  exports.default = _default;
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/link-picker",
3
- "version": "1.9.2",
3
+ "version": "1.9.3",
4
4
  "sideEffects": false
5
5
  }
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Generates analytics utilities from the package analytics spec yaml
5
5
  *
6
- * @codegen <<SignedSource::115bafba068f36f76da67200ee2d08c4>>
6
+ * @codegen <<SignedSource::e4f7029050ef6edde92cac3d9535cd4a>>
7
7
  * @codegenCommand yarn workspace @atlaskit/link-picker run codegen-analytics
8
8
  */
9
9
  function createEventPayload(eventKey, attributes) {
@@ -0,0 +1,95 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import React, { useCallback, useContext, useMemo, useRef, useState } from 'react';
3
+ import { AnalyticsListener } from '@atlaskit/analytics-next';
4
+ import { ANALYTICS_CHANNEL } from '../constants';
5
+ import { normalizeUrl } from '../utils/url';
6
+ const DEFAULT_CONTEXT_ATTRIBUTES = {
7
+ linkState: 'newLink',
8
+ linkFieldContent: null,
9
+ linkFieldContentInputMethod: null,
10
+ displayTextFieldContent: null,
11
+ displayTextFieldContentInputMethod: null
12
+ };
13
+ const LinkPickerAnalyticsContext = /*#__PURE__*/React.createContext({
14
+ trackAttribute: () => {},
15
+ trackAttributes: () => {},
16
+ getAttributes: () => DEFAULT_CONTEXT_ATTRIBUTES
17
+ });
18
+ /**
19
+ * TODO: Should we hook this into the form state/reducer?
20
+ */
21
+
22
+ const contextAttributesFromIntialProps = props => {
23
+ return {
24
+ linkState: normalizeUrl(props.url) ? 'editLink' : 'newLink',
25
+ linkFieldContent: normalizeUrl(props.url) ? 'url' : null,
26
+ displayTextFieldContent: Boolean(props.displayText) ? 'text_string' : null
27
+ };
28
+ };
29
+ /**
30
+ * Context for tracking attributes to deliver at a context-level
31
+ */
32
+
33
+
34
+ const LinkPickerAnalytics = ({
35
+ initialContext,
36
+ children
37
+ }) => {
38
+ const dataRef = useRef({ ...DEFAULT_CONTEXT_ATTRIBUTES,
39
+ ...initialContext
40
+ });
41
+ const methods = useMemo(() => ({
42
+ trackAttribute: (key, value) => {
43
+ dataRef.current[key] = value;
44
+ },
45
+ trackAttributes: attributes => {
46
+ dataRef.current = { ...dataRef.current,
47
+ ...attributes
48
+ };
49
+ },
50
+ getAttributes: () => dataRef.current
51
+ }), []);
52
+ return /*#__PURE__*/React.createElement(LinkPickerAnalyticsContext.Provider, {
53
+ value: methods
54
+ }, children);
55
+ };
56
+ /**
57
+ * Hook that exposes the context-level attribute getters and setters.
58
+ */
59
+
60
+
61
+ export const useLinkPickerAnalytics = () => useContext(LinkPickerAnalyticsContext);
62
+ /**
63
+ * Wrap component in "attributes" context store and initialise the initial context attributes from props.
64
+ */
65
+
66
+ function withLinkPickerAnalytics(WrappedComponent) {
67
+ return props => {
68
+ const [initialContext] = useState(() => contextAttributesFromIntialProps(props));
69
+ return /*#__PURE__*/React.createElement(LinkPickerAnalytics, _extends({
70
+ initialContext: initialContext
71
+ }, props), /*#__PURE__*/React.createElement(WrappedComponent, props));
72
+ };
73
+ }
74
+ /**
75
+ * Wraps a component with the analytics context + listener to update events with contextual-level attributes.
76
+ * Should be implemented once at the root of the link picker.
77
+ */
78
+
79
+
80
+ export function withLinkPickerAnalyticsContext(WrappedComponent) {
81
+ return withLinkPickerAnalytics(props => {
82
+ const {
83
+ getAttributes
84
+ } = useLinkPickerAnalytics();
85
+ const onEvent = useCallback(event => {
86
+ event.update({
87
+ attributes: getAttributes()
88
+ });
89
+ }, [getAttributes]);
90
+ return /*#__PURE__*/React.createElement(AnalyticsListener, {
91
+ channel: ANALYTICS_CHANNEL,
92
+ onEvent: onEvent
93
+ }, /*#__PURE__*/React.createElement(WrappedComponent, props));
94
+ });
95
+ }