@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.
- package/CHANGELOG.md +12 -0
- package/dist/cjs/analytics.codegen.js +1 -1
- package/dist/cjs/common/analytics/context.js +137 -0
- package/dist/cjs/common/analytics/index.js +27 -0
- package/dist/cjs/common/analytics/input-field-tracking.js +115 -0
- package/dist/cjs/common/constants.js +1 -1
- package/dist/cjs/ui/error-boundary/index.js +104 -0
- package/dist/cjs/ui/index.js +10 -6
- package/dist/cjs/ui/link-picker/index.js +56 -16
- package/dist/cjs/ui/link-picker/text-input/index.js +18 -23
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/analytics.codegen.js +1 -1
- package/dist/es2019/common/analytics/context.js +95 -0
- package/dist/es2019/common/analytics/index.js +2 -0
- package/dist/es2019/common/analytics/input-field-tracking.js +85 -0
- package/dist/es2019/common/constants.js +1 -1
- package/dist/es2019/ui/error-boundary/index.js +62 -0
- package/dist/es2019/ui/index.js +8 -5
- package/dist/es2019/ui/link-picker/index.js +51 -14
- package/dist/es2019/ui/link-picker/link-search-error/index.js +2 -2
- package/dist/es2019/ui/link-picker/text-input/index.js +14 -20
- package/dist/es2019/version.json +1 -1
- package/dist/esm/analytics.codegen.js +1 -1
- package/dist/esm/common/analytics/context.js +110 -0
- package/dist/esm/common/analytics/index.js +2 -0
- package/dist/esm/common/analytics/input-field-tracking.js +91 -0
- package/dist/esm/common/constants.js +1 -1
- package/dist/esm/ui/error-boundary/index.js +81 -0
- package/dist/esm/ui/index.js +8 -5
- package/dist/esm/ui/link-picker/index.js +54 -14
- package/dist/esm/ui/link-picker/link-search-error/index.js +2 -2
- package/dist/esm/ui/link-picker/text-input/index.js +16 -21
- package/dist/esm/version.json +1 -1
- package/dist/types/analytics.codegen.d.ts +21 -1
- package/dist/types/common/analytics/context.d.ts +28 -0
- package/dist/types/common/analytics/index.d.ts +2 -0
- package/dist/types/common/analytics/input-field-tracking.d.ts +17 -0
- package/dist/types/common/constants.d.ts +1 -1
- package/dist/types/ui/error-boundary/index.d.ts +27 -0
- package/dist/types/ui/index.d.ts +2 -5
- package/dist/types/ui/link-picker/text-input/index.d.ts +6 -8
- package/package.json +1 -1
- package/dist/cjs/services/with-link-picker-analytics/index.js +0 -33
- package/dist/es2019/services/with-link-picker-analytics/index.js +0 -15
- package/dist/esm/services/with-link-picker-analytics/index.js +0 -17
- 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,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 = '
|
|
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;
|
package/dist/esm/ui/index.js
CHANGED
|
@@ -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(
|
|
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 (
|
|
148
|
+
var handleChangeUrl = useCallback(function (e) {
|
|
125
149
|
dispatch({
|
|
126
|
-
url:
|
|
150
|
+
url: e.currentTarget.value
|
|
127
151
|
});
|
|
128
152
|
}, []);
|
|
129
|
-
var handleChangeText = useCallback(function (
|
|
153
|
+
var handleChangeText = useCallback(function (e) {
|
|
130
154
|
dispatch({
|
|
131
|
-
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(); //
|
|
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(
|
|
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(
|
|
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", "
|
|
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
|
|
17
|
-
var name =
|
|
18
|
-
label =
|
|
19
|
-
autoFocus =
|
|
20
|
-
onRedo =
|
|
21
|
-
onUndo =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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 (
|
|
83
|
-
var 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
|
|
91
|
+
export default TextInput;
|
package/dist/esm/version.json
CHANGED
|
@@ -3,19 +3,39 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Generates analytics utilities from the package analytics spec yaml
|
|
5
5
|
*
|
|
6
|
-
* @codegen <<SignedSource::
|
|
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 {};
|