@atlaskit/editor-plugin-media-insert 25.0.1 → 25.0.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @atlaskit/editor-plugin-media-insert
2
2
 
3
+ ## 25.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`39abc4f2636bc`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/39abc4f2636bc) -
8
+ Add selected tab metadata, source context, opened tab metadata, and initial/tab-change viewed
9
+ events to media insert picker analytics.
10
+ - [`383a8b7cb9b90`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/383a8b7cb9b90) -
11
+ [ux] Ungates the media insert picker tab for image generation relative to the gate for analytics
12
+ tab. This is is to continue the behavior without the analytics changes when the gate is off.
13
+ Prevents heavy coupling with the gate.
14
+ - Updated dependencies
15
+
16
+ ## 25.0.2
17
+
18
+ ### Patch Changes
19
+
20
+ - Updated dependencies
21
+
3
22
  ## 25.0.1
4
23
 
5
24
  ### Patch Changes
@@ -13,6 +13,14 @@ var _actions = require("./pm-plugins/actions");
13
13
  var _main = require("./pm-plugins/main");
14
14
  var _pluginKey = require("./pm-plugins/plugin-key");
15
15
  var _MediaInsertPicker = require("./ui/MediaInsertPicker");
16
+ var getInitialMediaInsertTab = function getInitialMediaInsertTab(registeredTabs, isOnlyExternalLinks) {
17
+ var registeredTab = registeredTabs[0];
18
+ if (registeredTab) {
19
+ return registeredTab.key;
20
+ }
21
+ return isOnlyExternalLinks ? _analytics.MEDIA_INSERT_TAB.LINK : _analytics.MEDIA_INSERT_TAB.UPLOAD;
22
+ };
23
+
16
24
  /**
17
25
  * Per-editor-instance registry of insert tabs registered via
18
26
  * `actions.registerInsertTab(...)`. Idempotent on `key` so that re-registering
@@ -187,7 +195,8 @@ var mediaInsertPlugin = exports.mediaInsertPlugin = function mediaInsertPlugin(_
187
195
  actionSubject: _analytics.ACTION_SUBJECT.PICKER,
188
196
  actionSubjectId: _analytics.ACTION_SUBJECT_ID.PICKER_MEDIA,
189
197
  attributes: {
190
- inputMethod: _analytics.INPUT_METHOD.QUICK_INSERT
198
+ inputMethod: _analytics.INPUT_METHOD.QUICK_INSERT,
199
+ openedTab: getInitialMediaInsertTab(insertTabRegistry.getAll(), config === null || config === void 0 ? void 0 : config.isOnlyExternalLinks)
191
200
  },
192
201
  eventType: _analytics.EVENT_TYPE.UI
193
202
  })(tr);
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
8
  exports.MediaInsertPicker = void 0;
9
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
10
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
10
11
  var _react = _interopRequireDefault(require("react"));
11
12
  var _reactIntl = require("react-intl");
@@ -15,6 +16,7 @@ var _hooks = require("@atlaskit/editor-common/hooks");
15
16
  var _messages = require("@atlaskit/editor-common/messages");
16
17
  var _ui = require("@atlaskit/editor-common/ui");
17
18
  var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
19
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
18
20
  var _compiled = require("@atlaskit/primitives/compiled");
19
21
  var _tabs = _interopRequireWildcard(require("@atlaskit/tabs"));
20
22
  var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
@@ -24,15 +26,81 @@ var _LocalMedia = require("./LocalMedia");
24
26
  var _MediaFromURL = require("./MediaFromURL");
25
27
  var _MediaInsertWrapper = require("./MediaInsertWrapper");
26
28
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
29
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
30
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
27
31
  var PopupWithListeners = (0, _ui.withOuterListeners)(_ui.Popup);
32
+ var MEDIA_INSERT_PICKER_ANALYTICS_SOURCE = 'MediaInsertPicker';
33
+ var EMPTY_REGISTERED_TABS = [];
34
+ var getMediaInsertPickerTabSource = function getMediaInsertPickerTabSource(selectedTab) {
35
+ return "".concat(MEDIA_INSERT_PICKER_ANALYTICS_SOURCE, " - ").concat(selectedTab);
36
+ };
37
+ var getNextTabIndexForKey = function getNextTabIndexForKey(currentTabIndex, tabCount, key) {
38
+ if (key === 'Home') {
39
+ return 0;
40
+ }
41
+ if (key === 'End') {
42
+ return tabCount - 1;
43
+ }
44
+ if (key === 'ArrowRight') {
45
+ return currentTabIndex === tabCount - 1 ? 0 : currentTabIndex + 1;
46
+ }
47
+ if (key === 'ArrowLeft') {
48
+ return currentTabIndex === 0 ? tabCount - 1 : currentTabIndex - 1;
49
+ }
50
+ return undefined;
51
+ };
52
+ var AnalyticsTab = function AnalyticsTab(_ref) {
53
+ var children = _ref.children,
54
+ onSelectTabForAnalytics = _ref.onSelectTabForAnalytics,
55
+ selectedTabIndex = _ref.selectedTabIndex,
56
+ tabCount = _ref.tabCount;
57
+ var _useTab = (0, _tabs.useTab)(),
58
+ onClick = _useTab.onClick,
59
+ id = _useTab.id,
60
+ ariaControls = _useTab['aria-controls'],
61
+ ariaPosinset = _useTab['aria-posinset'],
62
+ ariaSelected = _useTab['aria-selected'],
63
+ ariaSetsize = _useTab['aria-setsize'],
64
+ onKeyDown = _useTab.onKeyDown,
65
+ role = _useTab.role,
66
+ tabIndex = _useTab.tabIndex;
67
+ var handleClick = _react.default.useCallback(function () {
68
+ onSelectTabForAnalytics(selectedTabIndex);
69
+ onClick();
70
+ }, [onClick, onSelectTabForAnalytics, selectedTabIndex]);
71
+ var handleKeyDown = _react.default.useCallback(function (event) {
72
+ var nextTabIndex = getNextTabIndexForKey(selectedTabIndex, tabCount, event.key);
73
+ if (nextTabIndex !== undefined) {
74
+ onSelectTabForAnalytics(nextTabIndex);
75
+ }
76
+ onKeyDown(event);
77
+ }, [onKeyDown, onSelectTabForAnalytics, selectedTabIndex, tabCount]);
78
+ return /*#__PURE__*/_react.default.createElement(_compiled.Focusable, {
79
+ as: "div",
80
+ isInset: true,
81
+ onClick: handleClick,
82
+ id: id,
83
+ "aria-controls": ariaControls,
84
+ "aria-posinset": ariaPosinset,
85
+ "aria-selected": ariaSelected,
86
+ "aria-setsize": ariaSetsize,
87
+ onKeyDown: handleKeyDown,
88
+ role: role,
89
+ tabIndex: tabIndex
90
+ }, /*#__PURE__*/_react.default.createElement(_compiled.Text, {
91
+ weight: "medium",
92
+ color: "inherit",
93
+ maxLines: 1
94
+ }, children));
95
+ };
28
96
 
29
97
  /**
30
98
  * A custom TabPanel that is non-focusable.
31
99
  */
32
- var CustomTabPanel = function CustomTabPanel(_ref) {
33
- var children = _ref.children,
34
- _ref$disablePaddingBl = _ref.disablePaddingBlockEnd,
35
- disablePaddingBlockEnd = _ref$disablePaddingBl === void 0 ? false : _ref$disablePaddingBl;
100
+ var CustomTabPanel = function CustomTabPanel(_ref2) {
101
+ var children = _ref2.children,
102
+ _ref2$disablePaddingB = _ref2.disablePaddingBlockEnd,
103
+ disablePaddingBlockEnd = _ref2$disablePaddingB === void 0 ? false : _ref2$disablePaddingB;
36
104
  var tabPanelAttributes = (0, _tabs.useTabPanel)();
37
105
  return /*#__PURE__*/_react.default.createElement(_compiled.Box, (0, _extends2.default)({
38
106
  paddingBlockEnd: disablePaddingBlockEnd ? 'space.0' : 'space.150'
@@ -42,26 +110,27 @@ var CustomTabPanel = function CustomTabPanel(_ref) {
42
110
  tabIndex: -1
43
111
  }), children);
44
112
  };
45
- var MediaInsertPicker = exports.MediaInsertPicker = function MediaInsertPicker(_ref2) {
113
+ var MediaInsertPicker = exports.MediaInsertPicker = function MediaInsertPicker(_ref3) {
46
114
  var _api$mediaInsert$acti, _api$mediaInsert, _api$mediaInsert$getI;
47
- var api = _ref2.api,
48
- editorView = _ref2.editorView,
49
- dispatchAnalyticsEvent = _ref2.dispatchAnalyticsEvent,
50
- popupsMountPoint = _ref2.popupsMountPoint,
51
- popupsBoundariesElement = _ref2.popupsBoundariesElement,
52
- popupsScrollableElement = _ref2.popupsScrollableElement,
53
- _closeMediaInsertPicker = _ref2.closeMediaInsertPicker,
54
- insertMediaSingle = _ref2.insertMediaSingle,
55
- insertExternalMediaSingle = _ref2.insertExternalMediaSingle,
56
- insertFile = _ref2.insertFile,
57
- _ref2$isOnlyExternalL = _ref2.isOnlyExternalLinks,
58
- isOnlyExternalLinks = _ref2$isOnlyExternalL === void 0 ? false : _ref2$isOnlyExternalL,
59
- customizedUrlValidation = _ref2.customizedUrlValidation,
60
- customizedHelperMessage = _ref2.customizedHelperMessage;
115
+ var api = _ref3.api,
116
+ editorView = _ref3.editorView,
117
+ dispatchAnalyticsEvent = _ref3.dispatchAnalyticsEvent,
118
+ popupsMountPoint = _ref3.popupsMountPoint,
119
+ popupsBoundariesElement = _ref3.popupsBoundariesElement,
120
+ popupsScrollableElement = _ref3.popupsScrollableElement,
121
+ _closeMediaInsertPicker = _ref3.closeMediaInsertPicker,
122
+ insertMediaSingle = _ref3.insertMediaSingle,
123
+ insertExternalMediaSingle = _ref3.insertExternalMediaSingle,
124
+ insertFile = _ref3.insertFile,
125
+ _ref3$isOnlyExternalL = _ref3.isOnlyExternalLinks,
126
+ isOnlyExternalLinks = _ref3$isOnlyExternalL === void 0 ? false : _ref3$isOnlyExternalL,
127
+ customizedUrlValidation = _ref3.customizedUrlValidation,
128
+ customizedHelperMessage = _ref3.customizedHelperMessage;
129
+ var isMediaInsertTabWithAnalyticsEnabled = (0, _platformFeatureFlags.fg)('media_insert_tab_with_analytics');
61
130
  // Tabs registered by other plugins via `api.mediaInsert.actions.registerInsertTab(...)`.
62
131
  // Read once per render; the registry is mutated only at plugin setup time so this is stable
63
132
  // for the lifetime of an editor instance.
64
- var registeredTabs = (_api$mediaInsert$acti = api === null || api === void 0 || (_api$mediaInsert = api.mediaInsert) === null || _api$mediaInsert === void 0 || (_api$mediaInsert = _api$mediaInsert.actions) === null || _api$mediaInsert === void 0 || (_api$mediaInsert$getI = _api$mediaInsert.getInsertTabs) === null || _api$mediaInsert$getI === void 0 ? void 0 : _api$mediaInsert$getI.call(_api$mediaInsert)) !== null && _api$mediaInsert$acti !== void 0 ? _api$mediaInsert$acti : [];
133
+ var registeredTabs = (_api$mediaInsert$acti = api === null || api === void 0 || (_api$mediaInsert = api.mediaInsert) === null || _api$mediaInsert === void 0 || (_api$mediaInsert = _api$mediaInsert.actions) === null || _api$mediaInsert === void 0 || (_api$mediaInsert$getI = _api$mediaInsert.getInsertTabs) === null || _api$mediaInsert$getI === void 0 ? void 0 : _api$mediaInsert$getI.call(_api$mediaInsert)) !== null && _api$mediaInsert$acti !== void 0 ? _api$mediaInsert$acti : EMPTY_REGISTERED_TABS;
65
134
  var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['media', 'mediaInsert'], function (states) {
66
135
  var _states$mediaState, _states$mediaInsertSt, _states$mediaInsertSt2;
67
136
  return {
@@ -91,6 +160,96 @@ var MediaInsertPicker = exports.MediaInsertPicker = function MediaInsertPicker(_
91
160
  var _useUnholyAutofocus = (0, _useUnholyAutofocus2.useUnholyAutofocus)(),
92
161
  autofocusRef = _useUnholyAutofocus.autofocusRef,
93
162
  onPositionCalculated = _useUnholyAutofocus.onPositionCalculated;
163
+ var tabCount = isMediaInsertTabWithAnalyticsEnabled ? registeredTabs.length + (isOnlyExternalLinks ? 1 : 2) : 0;
164
+ var getTabAnalyticsMetadata = _react.default.useCallback(function (selectedTabIndex) {
165
+ var registeredTab = registeredTabs[selectedTabIndex];
166
+ if (registeredTab) {
167
+ return {
168
+ selectedTab: registeredTab.key,
169
+ selectedTabIndex: selectedTabIndex
170
+ };
171
+ }
172
+ var uploadTabIndex = registeredTabs.length;
173
+ if (!isOnlyExternalLinks && selectedTabIndex === uploadTabIndex) {
174
+ return {
175
+ selectedTab: _analytics.MEDIA_INSERT_TAB.UPLOAD,
176
+ selectedTabIndex: selectedTabIndex
177
+ };
178
+ }
179
+ var linkTabIndex = registeredTabs.length + (isOnlyExternalLinks ? 0 : 1);
180
+ if (selectedTabIndex === linkTabIndex) {
181
+ return {
182
+ selectedTab: _analytics.MEDIA_INSERT_TAB.LINK,
183
+ selectedTabIndex: selectedTabIndex
184
+ };
185
+ }
186
+ return {
187
+ selectedTab: 'unknown',
188
+ selectedTabIndex: selectedTabIndex
189
+ };
190
+ }, [isOnlyExternalLinks, registeredTabs]);
191
+ var selectedTabAnalyticsMetadataRef = _react.default.useRef(getTabAnalyticsMetadata(0));
192
+ var tabsAnalyticsContext = _react.default.useMemo(function () {
193
+ return {
194
+ get source() {
195
+ return getMediaInsertPickerTabSource(selectedTabAnalyticsMetadataRef.current.selectedTab);
196
+ }
197
+ };
198
+ }, []);
199
+ var setSelectedTabAnalyticsMetadata = _react.default.useCallback(function (selectedTabIndex) {
200
+ selectedTabAnalyticsMetadataRef.current = getTabAnalyticsMetadata(selectedTabIndex);
201
+ }, [getTabAnalyticsMetadata]);
202
+ var hasDispatchedInitialTabViewedEventRef = _react.default.useRef(false);
203
+ _react.default.useEffect(function () {
204
+ if (!isMediaInsertTabWithAnalyticsEnabled || !isOpen) {
205
+ hasDispatchedInitialTabViewedEventRef.current = false;
206
+ return;
207
+ }
208
+ if (!mediaProvider || !dispatchAnalyticsEvent || hasDispatchedInitialTabViewedEventRef.current) {
209
+ return;
210
+ }
211
+ var selectedTabMetadata = getTabAnalyticsMetadata(0);
212
+ selectedTabAnalyticsMetadataRef.current = selectedTabMetadata;
213
+ // Atlaskit Tabs only calls `onChange` after the user changes tabs. Fire the
214
+ // initial viewed event once per picker open so the first rendered tab is also tracked.
215
+ var payload = {
216
+ action: _analytics.ACTION.VIEWED,
217
+ actionSubject: _analytics.ACTION_SUBJECT.PICKER,
218
+ actionSubjectId: _analytics.ACTION_SUBJECT_ID.PICKER_MEDIA,
219
+ eventType: _analytics.EVENT_TYPE.UI,
220
+ attributes: {
221
+ selectedTab: selectedTabMetadata.selectedTab,
222
+ selectedTabIndex: selectedTabMetadata.selectedTabIndex
223
+ }
224
+ };
225
+ dispatchAnalyticsEvent(payload);
226
+ hasDispatchedInitialTabViewedEventRef.current = true;
227
+ }, [dispatchAnalyticsEvent, getTabAnalyticsMetadata, isMediaInsertTabWithAnalyticsEnabled, isOpen, mediaProvider]);
228
+ var handleTabChange = _react.default.useCallback(function (selectedTabIndex, analyticsEvent) {
229
+ var selectedTabMetadata = getTabAnalyticsMetadata(selectedTabIndex);
230
+ selectedTabAnalyticsMetadataRef.current = selectedTabMetadata;
231
+ analyticsEvent.update(function (payload) {
232
+ return _objectSpread(_objectSpread({}, payload), {}, {
233
+ attributes: _objectSpread(_objectSpread({}, payload.attributes), {}, {
234
+ selectedTab: selectedTabMetadata.selectedTab,
235
+ selectedTabIndex: selectedTabIndex
236
+ })
237
+ });
238
+ }).fire();
239
+ if (dispatchAnalyticsEvent) {
240
+ var payload = {
241
+ action: _analytics.ACTION.VIEWED,
242
+ actionSubject: _analytics.ACTION_SUBJECT.PICKER,
243
+ actionSubjectId: _analytics.ACTION_SUBJECT_ID.PICKER_MEDIA,
244
+ eventType: _analytics.EVENT_TYPE.UI,
245
+ attributes: {
246
+ selectedTab: selectedTabMetadata.selectedTab,
247
+ selectedTabIndex: selectedTabIndex
248
+ }
249
+ };
250
+ dispatchAnalyticsEvent(payload);
251
+ }
252
+ }, [dispatchAnalyticsEvent, getTabAnalyticsMetadata]);
94
253
  if (!isOpen || !mediaProvider) {
95
254
  return null;
96
255
  }
@@ -119,6 +278,7 @@ var MediaInsertPicker = exports.MediaInsertPicker = function MediaInsertPicker(_
119
278
  };
120
279
  };
121
280
  var fileTabTitle = (0, _expValEqualsNoExposure.expValEqualsNoExposure)('cc_page_experiences_editor_image_generation', 'isEnabled', true) ? intl.formatMessage(_messages.mediaInsertMessages.uploadTabTitle) : intl.formatMessage(_messages.mediaInsertMessages.fileTabTitle);
281
+ var linkTabIndex = isMediaInsertTabWithAnalyticsEnabled ? registeredTabs.length + (isOnlyExternalLinks ? 0 : 1) : 0;
122
282
  return /*#__PURE__*/_react.default.createElement(PopupWithListeners, {
123
283
  ariaLabel: intl.formatMessage(_messages.mediaInsertMessages.mediaPickerPopupAriaLabel)
124
284
  // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
@@ -140,16 +300,33 @@ var MediaInsertPicker = exports.MediaInsertPicker = function MediaInsertPicker(_
140
300
  return /*#__PURE__*/_react.default.createElement(_MediaInsertWrapper.MediaInsertWrapper, {
141
301
  ref: setOutsideClickTargetRef
142
302
  }, /*#__PURE__*/_react.default.createElement(_tabs.default, {
143
- id: "media-insert-tab-navigation"
303
+ id: "media-insert-tab-navigation",
304
+ analyticsContext: isMediaInsertTabWithAnalyticsEnabled ? tabsAnalyticsContext : undefined,
305
+ onChange: isMediaInsertTabWithAnalyticsEnabled ? handleTabChange : undefined
144
306
  }, /*#__PURE__*/_react.default.createElement(_compiled.Box, {
145
307
  paddingBlockEnd: "space.150"
146
- }, /*#__PURE__*/_react.default.createElement(_tabs.TabList, null, registeredTabs.map(function (tab) {
308
+ }, /*#__PURE__*/_react.default.createElement(_tabs.TabList, null, isMediaInsertTabWithAnalyticsEnabled && registeredTabs.map(function (tab, index) {
309
+ return /*#__PURE__*/_react.default.createElement(AnalyticsTab, {
310
+ key: tab.key,
311
+ onSelectTabForAnalytics: setSelectedTabAnalyticsMetadata,
312
+ selectedTabIndex: index,
313
+ tabCount: tabCount
314
+ }, tab.label);
315
+ }), isMediaInsertTabWithAnalyticsEnabled && !isOnlyExternalLinks && /*#__PURE__*/_react.default.createElement(AnalyticsTab, {
316
+ onSelectTabForAnalytics: setSelectedTabAnalyticsMetadata,
317
+ selectedTabIndex: registeredTabs.length,
318
+ tabCount: tabCount
319
+ }, fileTabTitle), isMediaInsertTabWithAnalyticsEnabled && /*#__PURE__*/_react.default.createElement(AnalyticsTab, {
320
+ onSelectTabForAnalytics: setSelectedTabAnalyticsMetadata,
321
+ selectedTabIndex: linkTabIndex,
322
+ tabCount: tabCount
323
+ }, intl.formatMessage(_messages.mediaInsertMessages.linkTabTitle)), !isMediaInsertTabWithAnalyticsEnabled && registeredTabs.map(function (tab) {
147
324
  return /*#__PURE__*/_react.default.createElement(_tabs.Tab, {
148
325
  key: tab.key
149
326
  }, tab.label);
150
- }), !isOnlyExternalLinks && /*#__PURE__*/_react.default.createElement(_tabs.Tab, null, fileTabTitle), /*#__PURE__*/_react.default.createElement(_tabs.Tab, null, intl.formatMessage(_messages.mediaInsertMessages.linkTabTitle)))), registeredTabs.map(function (_ref3) {
151
- var key = _ref3.key,
152
- TabComponent = _ref3.component;
327
+ }), !isMediaInsertTabWithAnalyticsEnabled && !isOnlyExternalLinks && /*#__PURE__*/_react.default.createElement(_tabs.Tab, null, fileTabTitle), !isMediaInsertTabWithAnalyticsEnabled && /*#__PURE__*/_react.default.createElement(_tabs.Tab, null, intl.formatMessage(_messages.mediaInsertMessages.linkTabTitle)))), registeredTabs.map(function (_ref4) {
328
+ var key = _ref4.key,
329
+ TabComponent = _ref4.component;
153
330
  return /*#__PURE__*/_react.default.createElement(CustomTabPanel, {
154
331
  key: key,
155
332
  disablePaddingBlockEnd: true
@@ -1,11 +1,18 @@
1
1
  import React from 'react';
2
- import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD, INSERT_MEDIA_VIA } from '@atlaskit/editor-common/analytics';
2
+ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD, INSERT_MEDIA_VIA, MEDIA_INSERT_TAB } from '@atlaskit/editor-common/analytics';
3
3
  import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
4
4
  import { IconImages } from '@atlaskit/editor-common/quick-insert';
5
5
  import { closeMediaInsertPicker, showMediaInsertPopup } from './pm-plugins/actions';
6
6
  import { createPlugin } from './pm-plugins/main';
7
7
  import { pluginKey } from './pm-plugins/plugin-key';
8
8
  import { MediaInsertPicker } from './ui/MediaInsertPicker';
9
+ const getInitialMediaInsertTab = (registeredTabs, isOnlyExternalLinks) => {
10
+ const registeredTab = registeredTabs[0];
11
+ if (registeredTab) {
12
+ return registeredTab.key;
13
+ }
14
+ return isOnlyExternalLinks ? MEDIA_INSERT_TAB.LINK : MEDIA_INSERT_TAB.UPLOAD;
15
+ };
9
16
 
10
17
  /**
11
18
  * Per-editor-instance registry of insert tabs registered via
@@ -172,7 +179,8 @@ export const mediaInsertPlugin = ({
172
179
  actionSubject: ACTION_SUBJECT.PICKER,
173
180
  actionSubjectId: ACTION_SUBJECT_ID.PICKER_MEDIA,
174
181
  attributes: {
175
- inputMethod: INPUT_METHOD.QUICK_INSERT
182
+ inputMethod: INPUT_METHOD.QUICK_INSERT,
183
+ openedTab: getInitialMediaInsertTab(insertTabRegistry.getAll(), config === null || config === void 0 ? void 0 : config.isOnlyExternalLinks)
176
184
  },
177
185
  eventType: EVENT_TYPE.UI
178
186
  })(tr);
@@ -1,14 +1,15 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
2
  import React from 'react';
3
3
  import { useIntl } from 'react-intl';
4
- import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
+ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD, MEDIA_INSERT_TAB } from '@atlaskit/editor-common/analytics';
5
5
  import { getDomRefFromSelection } from '@atlaskit/editor-common/get-dom-ref-from-selection';
6
6
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
7
7
  import { mediaInsertMessages } from '@atlaskit/editor-common/messages';
8
8
  import { PlainOutsideClickTargetRefContext, Popup, withOuterListeners } from '@atlaskit/editor-common/ui';
9
9
  import { akEditorFloatingDialogZIndex } from '@atlaskit/editor-shared-styles';
10
- import { Box } from '@atlaskit/primitives/compiled';
11
- import Tabs, { Tab, TabList, useTabPanel } from '@atlaskit/tabs';
10
+ import { fg } from '@atlaskit/platform-feature-flags';
11
+ import { Box, Focusable, Text } from '@atlaskit/primitives/compiled';
12
+ import Tabs, { Tab, TabList, useTab, useTabPanel } from '@atlaskit/tabs';
12
13
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
13
14
  import { useFocusEditor } from './hooks/use-focus-editor';
14
15
  import { useUnholyAutofocus } from './hooks/use-unholy-autofocus';
@@ -16,6 +17,70 @@ import { LocalMedia } from './LocalMedia';
16
17
  import { MediaFromURL } from './MediaFromURL';
17
18
  import { MediaInsertWrapper } from './MediaInsertWrapper';
18
19
  const PopupWithListeners = withOuterListeners(Popup);
20
+ const MEDIA_INSERT_PICKER_ANALYTICS_SOURCE = 'MediaInsertPicker';
21
+ const EMPTY_REGISTERED_TABS = [];
22
+ const getMediaInsertPickerTabSource = selectedTab => `${MEDIA_INSERT_PICKER_ANALYTICS_SOURCE} - ${selectedTab}`;
23
+ const getNextTabIndexForKey = (currentTabIndex, tabCount, key) => {
24
+ if (key === 'Home') {
25
+ return 0;
26
+ }
27
+ if (key === 'End') {
28
+ return tabCount - 1;
29
+ }
30
+ if (key === 'ArrowRight') {
31
+ return currentTabIndex === tabCount - 1 ? 0 : currentTabIndex + 1;
32
+ }
33
+ if (key === 'ArrowLeft') {
34
+ return currentTabIndex === 0 ? tabCount - 1 : currentTabIndex - 1;
35
+ }
36
+ return undefined;
37
+ };
38
+ const AnalyticsTab = ({
39
+ children,
40
+ onSelectTabForAnalytics,
41
+ selectedTabIndex,
42
+ tabCount
43
+ }) => {
44
+ const {
45
+ onClick,
46
+ id,
47
+ 'aria-controls': ariaControls,
48
+ 'aria-posinset': ariaPosinset,
49
+ 'aria-selected': ariaSelected,
50
+ 'aria-setsize': ariaSetsize,
51
+ onKeyDown,
52
+ role,
53
+ tabIndex
54
+ } = useTab();
55
+ const handleClick = React.useCallback(() => {
56
+ onSelectTabForAnalytics(selectedTabIndex);
57
+ onClick();
58
+ }, [onClick, onSelectTabForAnalytics, selectedTabIndex]);
59
+ const handleKeyDown = React.useCallback(event => {
60
+ const nextTabIndex = getNextTabIndexForKey(selectedTabIndex, tabCount, event.key);
61
+ if (nextTabIndex !== undefined) {
62
+ onSelectTabForAnalytics(nextTabIndex);
63
+ }
64
+ onKeyDown(event);
65
+ }, [onKeyDown, onSelectTabForAnalytics, selectedTabIndex, tabCount]);
66
+ return /*#__PURE__*/React.createElement(Focusable, {
67
+ as: "div",
68
+ isInset: true,
69
+ onClick: handleClick,
70
+ id: id,
71
+ "aria-controls": ariaControls,
72
+ "aria-posinset": ariaPosinset,
73
+ "aria-selected": ariaSelected,
74
+ "aria-setsize": ariaSetsize,
75
+ onKeyDown: handleKeyDown,
76
+ role: role,
77
+ tabIndex: tabIndex
78
+ }, /*#__PURE__*/React.createElement(Text, {
79
+ weight: "medium",
80
+ color: "inherit",
81
+ maxLines: 1
82
+ }, children));
83
+ };
19
84
 
20
85
  /**
21
86
  * A custom TabPanel that is non-focusable.
@@ -49,10 +114,11 @@ export const MediaInsertPicker = ({
49
114
  customizedHelperMessage
50
115
  }) => {
51
116
  var _api$mediaInsert$acti, _api$mediaInsert, _api$mediaInsert$acti2, _api$mediaInsert$acti3;
117
+ const isMediaInsertTabWithAnalyticsEnabled = fg('media_insert_tab_with_analytics');
52
118
  // Tabs registered by other plugins via `api.mediaInsert.actions.registerInsertTab(...)`.
53
119
  // Read once per render; the registry is mutated only at plugin setup time so this is stable
54
120
  // for the lifetime of an editor instance.
55
- const registeredTabs = (_api$mediaInsert$acti = api === null || api === void 0 ? void 0 : (_api$mediaInsert = api.mediaInsert) === null || _api$mediaInsert === void 0 ? void 0 : (_api$mediaInsert$acti2 = _api$mediaInsert.actions) === null || _api$mediaInsert$acti2 === void 0 ? void 0 : (_api$mediaInsert$acti3 = _api$mediaInsert$acti2.getInsertTabs) === null || _api$mediaInsert$acti3 === void 0 ? void 0 : _api$mediaInsert$acti3.call(_api$mediaInsert$acti2)) !== null && _api$mediaInsert$acti !== void 0 ? _api$mediaInsert$acti : [];
121
+ const registeredTabs = (_api$mediaInsert$acti = api === null || api === void 0 ? void 0 : (_api$mediaInsert = api.mediaInsert) === null || _api$mediaInsert === void 0 ? void 0 : (_api$mediaInsert$acti2 = _api$mediaInsert.actions) === null || _api$mediaInsert$acti2 === void 0 ? void 0 : (_api$mediaInsert$acti3 = _api$mediaInsert$acti2.getInsertTabs) === null || _api$mediaInsert$acti3 === void 0 ? void 0 : _api$mediaInsert$acti3.call(_api$mediaInsert$acti2)) !== null && _api$mediaInsert$acti !== void 0 ? _api$mediaInsert$acti : EMPTY_REGISTERED_TABS;
56
122
  const {
57
123
  mediaProvider,
58
124
  isOpen,
@@ -84,6 +150,94 @@ export const MediaInsertPicker = ({
84
150
  autofocusRef,
85
151
  onPositionCalculated
86
152
  } = useUnholyAutofocus();
153
+ const tabCount = isMediaInsertTabWithAnalyticsEnabled ? registeredTabs.length + (isOnlyExternalLinks ? 1 : 2) : 0;
154
+ const getTabAnalyticsMetadata = React.useCallback(selectedTabIndex => {
155
+ const registeredTab = registeredTabs[selectedTabIndex];
156
+ if (registeredTab) {
157
+ return {
158
+ selectedTab: registeredTab.key,
159
+ selectedTabIndex
160
+ };
161
+ }
162
+ const uploadTabIndex = registeredTabs.length;
163
+ if (!isOnlyExternalLinks && selectedTabIndex === uploadTabIndex) {
164
+ return {
165
+ selectedTab: MEDIA_INSERT_TAB.UPLOAD,
166
+ selectedTabIndex
167
+ };
168
+ }
169
+ const linkTabIndex = registeredTabs.length + (isOnlyExternalLinks ? 0 : 1);
170
+ if (selectedTabIndex === linkTabIndex) {
171
+ return {
172
+ selectedTab: MEDIA_INSERT_TAB.LINK,
173
+ selectedTabIndex
174
+ };
175
+ }
176
+ return {
177
+ selectedTab: 'unknown',
178
+ selectedTabIndex
179
+ };
180
+ }, [isOnlyExternalLinks, registeredTabs]);
181
+ const selectedTabAnalyticsMetadataRef = React.useRef(getTabAnalyticsMetadata(0));
182
+ const tabsAnalyticsContext = React.useMemo(() => ({
183
+ get source() {
184
+ return getMediaInsertPickerTabSource(selectedTabAnalyticsMetadataRef.current.selectedTab);
185
+ }
186
+ }), []);
187
+ const setSelectedTabAnalyticsMetadata = React.useCallback(selectedTabIndex => {
188
+ selectedTabAnalyticsMetadataRef.current = getTabAnalyticsMetadata(selectedTabIndex);
189
+ }, [getTabAnalyticsMetadata]);
190
+ const hasDispatchedInitialTabViewedEventRef = React.useRef(false);
191
+ React.useEffect(() => {
192
+ if (!isMediaInsertTabWithAnalyticsEnabled || !isOpen) {
193
+ hasDispatchedInitialTabViewedEventRef.current = false;
194
+ return;
195
+ }
196
+ if (!mediaProvider || !dispatchAnalyticsEvent || hasDispatchedInitialTabViewedEventRef.current) {
197
+ return;
198
+ }
199
+ const selectedTabMetadata = getTabAnalyticsMetadata(0);
200
+ selectedTabAnalyticsMetadataRef.current = selectedTabMetadata;
201
+ // Atlaskit Tabs only calls `onChange` after the user changes tabs. Fire the
202
+ // initial viewed event once per picker open so the first rendered tab is also tracked.
203
+ const payload = {
204
+ action: ACTION.VIEWED,
205
+ actionSubject: ACTION_SUBJECT.PICKER,
206
+ actionSubjectId: ACTION_SUBJECT_ID.PICKER_MEDIA,
207
+ eventType: EVENT_TYPE.UI,
208
+ attributes: {
209
+ selectedTab: selectedTabMetadata.selectedTab,
210
+ selectedTabIndex: selectedTabMetadata.selectedTabIndex
211
+ }
212
+ };
213
+ dispatchAnalyticsEvent(payload);
214
+ hasDispatchedInitialTabViewedEventRef.current = true;
215
+ }, [dispatchAnalyticsEvent, getTabAnalyticsMetadata, isMediaInsertTabWithAnalyticsEnabled, isOpen, mediaProvider]);
216
+ const handleTabChange = React.useCallback((selectedTabIndex, analyticsEvent) => {
217
+ const selectedTabMetadata = getTabAnalyticsMetadata(selectedTabIndex);
218
+ selectedTabAnalyticsMetadataRef.current = selectedTabMetadata;
219
+ analyticsEvent.update(payload => ({
220
+ ...payload,
221
+ attributes: {
222
+ ...payload.attributes,
223
+ selectedTab: selectedTabMetadata.selectedTab,
224
+ selectedTabIndex
225
+ }
226
+ })).fire();
227
+ if (dispatchAnalyticsEvent) {
228
+ const payload = {
229
+ action: ACTION.VIEWED,
230
+ actionSubject: ACTION_SUBJECT.PICKER,
231
+ actionSubjectId: ACTION_SUBJECT_ID.PICKER_MEDIA,
232
+ eventType: EVENT_TYPE.UI,
233
+ attributes: {
234
+ selectedTab: selectedTabMetadata.selectedTab,
235
+ selectedTabIndex
236
+ }
237
+ };
238
+ dispatchAnalyticsEvent(payload);
239
+ }
240
+ }, [dispatchAnalyticsEvent, getTabAnalyticsMetadata]);
87
241
  if (!isOpen || !mediaProvider) {
88
242
  return null;
89
243
  }
@@ -110,6 +264,7 @@ export const MediaInsertPicker = ({
110
264
  focusEditor();
111
265
  };
112
266
  const fileTabTitle = expValEqualsNoExposure('cc_page_experiences_editor_image_generation', 'isEnabled', true) ? intl.formatMessage(mediaInsertMessages.uploadTabTitle) : intl.formatMessage(mediaInsertMessages.fileTabTitle);
267
+ const linkTabIndex = isMediaInsertTabWithAnalyticsEnabled ? registeredTabs.length + (isOnlyExternalLinks ? 0 : 1) : 0;
113
268
  return /*#__PURE__*/React.createElement(PopupWithListeners, {
114
269
  ariaLabel: intl.formatMessage(mediaInsertMessages.mediaPickerPopupAriaLabel)
115
270
  // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
@@ -130,12 +285,27 @@ export const MediaInsertPicker = ({
130
285
  }, /*#__PURE__*/React.createElement(PlainOutsideClickTargetRefContext.Consumer, null, setOutsideClickTargetRef => /*#__PURE__*/React.createElement(MediaInsertWrapper, {
131
286
  ref: setOutsideClickTargetRef
132
287
  }, /*#__PURE__*/React.createElement(Tabs, {
133
- id: "media-insert-tab-navigation"
288
+ id: "media-insert-tab-navigation",
289
+ analyticsContext: isMediaInsertTabWithAnalyticsEnabled ? tabsAnalyticsContext : undefined,
290
+ onChange: isMediaInsertTabWithAnalyticsEnabled ? handleTabChange : undefined
134
291
  }, /*#__PURE__*/React.createElement(Box, {
135
292
  paddingBlockEnd: "space.150"
136
- }, /*#__PURE__*/React.createElement(TabList, null, registeredTabs.map(tab => /*#__PURE__*/React.createElement(Tab, {
293
+ }, /*#__PURE__*/React.createElement(TabList, null, isMediaInsertTabWithAnalyticsEnabled && registeredTabs.map((tab, index) => /*#__PURE__*/React.createElement(AnalyticsTab, {
294
+ key: tab.key,
295
+ onSelectTabForAnalytics: setSelectedTabAnalyticsMetadata,
296
+ selectedTabIndex: index,
297
+ tabCount: tabCount
298
+ }, tab.label)), isMediaInsertTabWithAnalyticsEnabled && !isOnlyExternalLinks && /*#__PURE__*/React.createElement(AnalyticsTab, {
299
+ onSelectTabForAnalytics: setSelectedTabAnalyticsMetadata,
300
+ selectedTabIndex: registeredTabs.length,
301
+ tabCount: tabCount
302
+ }, fileTabTitle), isMediaInsertTabWithAnalyticsEnabled && /*#__PURE__*/React.createElement(AnalyticsTab, {
303
+ onSelectTabForAnalytics: setSelectedTabAnalyticsMetadata,
304
+ selectedTabIndex: linkTabIndex,
305
+ tabCount: tabCount
306
+ }, intl.formatMessage(mediaInsertMessages.linkTabTitle)), !isMediaInsertTabWithAnalyticsEnabled && registeredTabs.map(tab => /*#__PURE__*/React.createElement(Tab, {
137
307
  key: tab.key
138
- }, tab.label)), !isOnlyExternalLinks && /*#__PURE__*/React.createElement(Tab, null, fileTabTitle), /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.linkTabTitle)))), registeredTabs.map(({
308
+ }, tab.label)), !isMediaInsertTabWithAnalyticsEnabled && !isOnlyExternalLinks && /*#__PURE__*/React.createElement(Tab, null, fileTabTitle), !isMediaInsertTabWithAnalyticsEnabled && /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.linkTabTitle)))), registeredTabs.map(({
139
309
  key,
140
310
  component: TabComponent
141
311
  }) => /*#__PURE__*/React.createElement(CustomTabPanel, {
@@ -1,11 +1,18 @@
1
1
  import React from 'react';
2
- import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD, INSERT_MEDIA_VIA } from '@atlaskit/editor-common/analytics';
2
+ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD, INSERT_MEDIA_VIA, MEDIA_INSERT_TAB } from '@atlaskit/editor-common/analytics';
3
3
  import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
4
4
  import { IconImages } from '@atlaskit/editor-common/quick-insert';
5
5
  import { closeMediaInsertPicker as _closeMediaInsertPicker, showMediaInsertPopup as _showMediaInsertPopup } from './pm-plugins/actions';
6
6
  import { createPlugin } from './pm-plugins/main';
7
7
  import { pluginKey } from './pm-plugins/plugin-key';
8
8
  import { MediaInsertPicker } from './ui/MediaInsertPicker';
9
+ var getInitialMediaInsertTab = function getInitialMediaInsertTab(registeredTabs, isOnlyExternalLinks) {
10
+ var registeredTab = registeredTabs[0];
11
+ if (registeredTab) {
12
+ return registeredTab.key;
13
+ }
14
+ return isOnlyExternalLinks ? MEDIA_INSERT_TAB.LINK : MEDIA_INSERT_TAB.UPLOAD;
15
+ };
9
16
 
10
17
  /**
11
18
  * Per-editor-instance registry of insert tabs registered via
@@ -181,7 +188,8 @@ export var mediaInsertPlugin = function mediaInsertPlugin(_ref) {
181
188
  actionSubject: ACTION_SUBJECT.PICKER,
182
189
  actionSubjectId: ACTION_SUBJECT_ID.PICKER_MEDIA,
183
190
  attributes: {
184
- inputMethod: INPUT_METHOD.QUICK_INSERT
191
+ inputMethod: INPUT_METHOD.QUICK_INSERT,
192
+ openedTab: getInitialMediaInsertTab(insertTabRegistry.getAll(), config === null || config === void 0 ? void 0 : config.isOnlyExternalLinks)
185
193
  },
186
194
  eventType: EVENT_TYPE.UI
187
195
  })(tr);
@@ -1,14 +1,18 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
1
2
  import _extends from "@babel/runtime/helpers/extends";
3
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
2
5
  import React from 'react';
3
6
  import { useIntl } from 'react-intl';
4
- import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
7
+ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD, MEDIA_INSERT_TAB } from '@atlaskit/editor-common/analytics';
5
8
  import { getDomRefFromSelection } from '@atlaskit/editor-common/get-dom-ref-from-selection';
6
9
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
7
10
  import { mediaInsertMessages } from '@atlaskit/editor-common/messages';
8
11
  import { PlainOutsideClickTargetRefContext, Popup, withOuterListeners } from '@atlaskit/editor-common/ui';
9
12
  import { akEditorFloatingDialogZIndex } from '@atlaskit/editor-shared-styles';
10
- import { Box } from '@atlaskit/primitives/compiled';
11
- import Tabs, { Tab, TabList, useTabPanel } from '@atlaskit/tabs';
13
+ import { fg } from '@atlaskit/platform-feature-flags';
14
+ import { Box, Focusable, Text } from '@atlaskit/primitives/compiled';
15
+ import Tabs, { Tab, TabList, useTab, useTabPanel } from '@atlaskit/tabs';
12
16
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
13
17
  import { useFocusEditor } from './hooks/use-focus-editor';
14
18
  import { useUnholyAutofocus } from './hooks/use-unholy-autofocus';
@@ -16,14 +20,78 @@ import { LocalMedia } from './LocalMedia';
16
20
  import { MediaFromURL } from './MediaFromURL';
17
21
  import { MediaInsertWrapper } from './MediaInsertWrapper';
18
22
  var PopupWithListeners = withOuterListeners(Popup);
23
+ var MEDIA_INSERT_PICKER_ANALYTICS_SOURCE = 'MediaInsertPicker';
24
+ var EMPTY_REGISTERED_TABS = [];
25
+ var getMediaInsertPickerTabSource = function getMediaInsertPickerTabSource(selectedTab) {
26
+ return "".concat(MEDIA_INSERT_PICKER_ANALYTICS_SOURCE, " - ").concat(selectedTab);
27
+ };
28
+ var getNextTabIndexForKey = function getNextTabIndexForKey(currentTabIndex, tabCount, key) {
29
+ if (key === 'Home') {
30
+ return 0;
31
+ }
32
+ if (key === 'End') {
33
+ return tabCount - 1;
34
+ }
35
+ if (key === 'ArrowRight') {
36
+ return currentTabIndex === tabCount - 1 ? 0 : currentTabIndex + 1;
37
+ }
38
+ if (key === 'ArrowLeft') {
39
+ return currentTabIndex === 0 ? tabCount - 1 : currentTabIndex - 1;
40
+ }
41
+ return undefined;
42
+ };
43
+ var AnalyticsTab = function AnalyticsTab(_ref) {
44
+ var children = _ref.children,
45
+ onSelectTabForAnalytics = _ref.onSelectTabForAnalytics,
46
+ selectedTabIndex = _ref.selectedTabIndex,
47
+ tabCount = _ref.tabCount;
48
+ var _useTab = useTab(),
49
+ onClick = _useTab.onClick,
50
+ id = _useTab.id,
51
+ ariaControls = _useTab['aria-controls'],
52
+ ariaPosinset = _useTab['aria-posinset'],
53
+ ariaSelected = _useTab['aria-selected'],
54
+ ariaSetsize = _useTab['aria-setsize'],
55
+ onKeyDown = _useTab.onKeyDown,
56
+ role = _useTab.role,
57
+ tabIndex = _useTab.tabIndex;
58
+ var handleClick = React.useCallback(function () {
59
+ onSelectTabForAnalytics(selectedTabIndex);
60
+ onClick();
61
+ }, [onClick, onSelectTabForAnalytics, selectedTabIndex]);
62
+ var handleKeyDown = React.useCallback(function (event) {
63
+ var nextTabIndex = getNextTabIndexForKey(selectedTabIndex, tabCount, event.key);
64
+ if (nextTabIndex !== undefined) {
65
+ onSelectTabForAnalytics(nextTabIndex);
66
+ }
67
+ onKeyDown(event);
68
+ }, [onKeyDown, onSelectTabForAnalytics, selectedTabIndex, tabCount]);
69
+ return /*#__PURE__*/React.createElement(Focusable, {
70
+ as: "div",
71
+ isInset: true,
72
+ onClick: handleClick,
73
+ id: id,
74
+ "aria-controls": ariaControls,
75
+ "aria-posinset": ariaPosinset,
76
+ "aria-selected": ariaSelected,
77
+ "aria-setsize": ariaSetsize,
78
+ onKeyDown: handleKeyDown,
79
+ role: role,
80
+ tabIndex: tabIndex
81
+ }, /*#__PURE__*/React.createElement(Text, {
82
+ weight: "medium",
83
+ color: "inherit",
84
+ maxLines: 1
85
+ }, children));
86
+ };
19
87
 
20
88
  /**
21
89
  * A custom TabPanel that is non-focusable.
22
90
  */
23
- var CustomTabPanel = function CustomTabPanel(_ref) {
24
- var children = _ref.children,
25
- _ref$disablePaddingBl = _ref.disablePaddingBlockEnd,
26
- disablePaddingBlockEnd = _ref$disablePaddingBl === void 0 ? false : _ref$disablePaddingBl;
91
+ var CustomTabPanel = function CustomTabPanel(_ref2) {
92
+ var children = _ref2.children,
93
+ _ref2$disablePaddingB = _ref2.disablePaddingBlockEnd,
94
+ disablePaddingBlockEnd = _ref2$disablePaddingB === void 0 ? false : _ref2$disablePaddingB;
27
95
  var tabPanelAttributes = useTabPanel();
28
96
  return /*#__PURE__*/React.createElement(Box, _extends({
29
97
  paddingBlockEnd: disablePaddingBlockEnd ? 'space.0' : 'space.150'
@@ -33,26 +101,27 @@ var CustomTabPanel = function CustomTabPanel(_ref) {
33
101
  tabIndex: -1
34
102
  }), children);
35
103
  };
36
- export var MediaInsertPicker = function MediaInsertPicker(_ref2) {
104
+ export var MediaInsertPicker = function MediaInsertPicker(_ref3) {
37
105
  var _api$mediaInsert$acti, _api$mediaInsert, _api$mediaInsert$getI;
38
- var api = _ref2.api,
39
- editorView = _ref2.editorView,
40
- dispatchAnalyticsEvent = _ref2.dispatchAnalyticsEvent,
41
- popupsMountPoint = _ref2.popupsMountPoint,
42
- popupsBoundariesElement = _ref2.popupsBoundariesElement,
43
- popupsScrollableElement = _ref2.popupsScrollableElement,
44
- _closeMediaInsertPicker = _ref2.closeMediaInsertPicker,
45
- insertMediaSingle = _ref2.insertMediaSingle,
46
- insertExternalMediaSingle = _ref2.insertExternalMediaSingle,
47
- insertFile = _ref2.insertFile,
48
- _ref2$isOnlyExternalL = _ref2.isOnlyExternalLinks,
49
- isOnlyExternalLinks = _ref2$isOnlyExternalL === void 0 ? false : _ref2$isOnlyExternalL,
50
- customizedUrlValidation = _ref2.customizedUrlValidation,
51
- customizedHelperMessage = _ref2.customizedHelperMessage;
106
+ var api = _ref3.api,
107
+ editorView = _ref3.editorView,
108
+ dispatchAnalyticsEvent = _ref3.dispatchAnalyticsEvent,
109
+ popupsMountPoint = _ref3.popupsMountPoint,
110
+ popupsBoundariesElement = _ref3.popupsBoundariesElement,
111
+ popupsScrollableElement = _ref3.popupsScrollableElement,
112
+ _closeMediaInsertPicker = _ref3.closeMediaInsertPicker,
113
+ insertMediaSingle = _ref3.insertMediaSingle,
114
+ insertExternalMediaSingle = _ref3.insertExternalMediaSingle,
115
+ insertFile = _ref3.insertFile,
116
+ _ref3$isOnlyExternalL = _ref3.isOnlyExternalLinks,
117
+ isOnlyExternalLinks = _ref3$isOnlyExternalL === void 0 ? false : _ref3$isOnlyExternalL,
118
+ customizedUrlValidation = _ref3.customizedUrlValidation,
119
+ customizedHelperMessage = _ref3.customizedHelperMessage;
120
+ var isMediaInsertTabWithAnalyticsEnabled = fg('media_insert_tab_with_analytics');
52
121
  // Tabs registered by other plugins via `api.mediaInsert.actions.registerInsertTab(...)`.
53
122
  // Read once per render; the registry is mutated only at plugin setup time so this is stable
54
123
  // for the lifetime of an editor instance.
55
- var registeredTabs = (_api$mediaInsert$acti = api === null || api === void 0 || (_api$mediaInsert = api.mediaInsert) === null || _api$mediaInsert === void 0 || (_api$mediaInsert = _api$mediaInsert.actions) === null || _api$mediaInsert === void 0 || (_api$mediaInsert$getI = _api$mediaInsert.getInsertTabs) === null || _api$mediaInsert$getI === void 0 ? void 0 : _api$mediaInsert$getI.call(_api$mediaInsert)) !== null && _api$mediaInsert$acti !== void 0 ? _api$mediaInsert$acti : [];
124
+ var registeredTabs = (_api$mediaInsert$acti = api === null || api === void 0 || (_api$mediaInsert = api.mediaInsert) === null || _api$mediaInsert === void 0 || (_api$mediaInsert = _api$mediaInsert.actions) === null || _api$mediaInsert === void 0 || (_api$mediaInsert$getI = _api$mediaInsert.getInsertTabs) === null || _api$mediaInsert$getI === void 0 ? void 0 : _api$mediaInsert$getI.call(_api$mediaInsert)) !== null && _api$mediaInsert$acti !== void 0 ? _api$mediaInsert$acti : EMPTY_REGISTERED_TABS;
56
125
  var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['media', 'mediaInsert'], function (states) {
57
126
  var _states$mediaState, _states$mediaInsertSt, _states$mediaInsertSt2;
58
127
  return {
@@ -82,6 +151,96 @@ export var MediaInsertPicker = function MediaInsertPicker(_ref2) {
82
151
  var _useUnholyAutofocus = useUnholyAutofocus(),
83
152
  autofocusRef = _useUnholyAutofocus.autofocusRef,
84
153
  onPositionCalculated = _useUnholyAutofocus.onPositionCalculated;
154
+ var tabCount = isMediaInsertTabWithAnalyticsEnabled ? registeredTabs.length + (isOnlyExternalLinks ? 1 : 2) : 0;
155
+ var getTabAnalyticsMetadata = React.useCallback(function (selectedTabIndex) {
156
+ var registeredTab = registeredTabs[selectedTabIndex];
157
+ if (registeredTab) {
158
+ return {
159
+ selectedTab: registeredTab.key,
160
+ selectedTabIndex: selectedTabIndex
161
+ };
162
+ }
163
+ var uploadTabIndex = registeredTabs.length;
164
+ if (!isOnlyExternalLinks && selectedTabIndex === uploadTabIndex) {
165
+ return {
166
+ selectedTab: MEDIA_INSERT_TAB.UPLOAD,
167
+ selectedTabIndex: selectedTabIndex
168
+ };
169
+ }
170
+ var linkTabIndex = registeredTabs.length + (isOnlyExternalLinks ? 0 : 1);
171
+ if (selectedTabIndex === linkTabIndex) {
172
+ return {
173
+ selectedTab: MEDIA_INSERT_TAB.LINK,
174
+ selectedTabIndex: selectedTabIndex
175
+ };
176
+ }
177
+ return {
178
+ selectedTab: 'unknown',
179
+ selectedTabIndex: selectedTabIndex
180
+ };
181
+ }, [isOnlyExternalLinks, registeredTabs]);
182
+ var selectedTabAnalyticsMetadataRef = React.useRef(getTabAnalyticsMetadata(0));
183
+ var tabsAnalyticsContext = React.useMemo(function () {
184
+ return {
185
+ get source() {
186
+ return getMediaInsertPickerTabSource(selectedTabAnalyticsMetadataRef.current.selectedTab);
187
+ }
188
+ };
189
+ }, []);
190
+ var setSelectedTabAnalyticsMetadata = React.useCallback(function (selectedTabIndex) {
191
+ selectedTabAnalyticsMetadataRef.current = getTabAnalyticsMetadata(selectedTabIndex);
192
+ }, [getTabAnalyticsMetadata]);
193
+ var hasDispatchedInitialTabViewedEventRef = React.useRef(false);
194
+ React.useEffect(function () {
195
+ if (!isMediaInsertTabWithAnalyticsEnabled || !isOpen) {
196
+ hasDispatchedInitialTabViewedEventRef.current = false;
197
+ return;
198
+ }
199
+ if (!mediaProvider || !dispatchAnalyticsEvent || hasDispatchedInitialTabViewedEventRef.current) {
200
+ return;
201
+ }
202
+ var selectedTabMetadata = getTabAnalyticsMetadata(0);
203
+ selectedTabAnalyticsMetadataRef.current = selectedTabMetadata;
204
+ // Atlaskit Tabs only calls `onChange` after the user changes tabs. Fire the
205
+ // initial viewed event once per picker open so the first rendered tab is also tracked.
206
+ var payload = {
207
+ action: ACTION.VIEWED,
208
+ actionSubject: ACTION_SUBJECT.PICKER,
209
+ actionSubjectId: ACTION_SUBJECT_ID.PICKER_MEDIA,
210
+ eventType: EVENT_TYPE.UI,
211
+ attributes: {
212
+ selectedTab: selectedTabMetadata.selectedTab,
213
+ selectedTabIndex: selectedTabMetadata.selectedTabIndex
214
+ }
215
+ };
216
+ dispatchAnalyticsEvent(payload);
217
+ hasDispatchedInitialTabViewedEventRef.current = true;
218
+ }, [dispatchAnalyticsEvent, getTabAnalyticsMetadata, isMediaInsertTabWithAnalyticsEnabled, isOpen, mediaProvider]);
219
+ var handleTabChange = React.useCallback(function (selectedTabIndex, analyticsEvent) {
220
+ var selectedTabMetadata = getTabAnalyticsMetadata(selectedTabIndex);
221
+ selectedTabAnalyticsMetadataRef.current = selectedTabMetadata;
222
+ analyticsEvent.update(function (payload) {
223
+ return _objectSpread(_objectSpread({}, payload), {}, {
224
+ attributes: _objectSpread(_objectSpread({}, payload.attributes), {}, {
225
+ selectedTab: selectedTabMetadata.selectedTab,
226
+ selectedTabIndex: selectedTabIndex
227
+ })
228
+ });
229
+ }).fire();
230
+ if (dispatchAnalyticsEvent) {
231
+ var payload = {
232
+ action: ACTION.VIEWED,
233
+ actionSubject: ACTION_SUBJECT.PICKER,
234
+ actionSubjectId: ACTION_SUBJECT_ID.PICKER_MEDIA,
235
+ eventType: EVENT_TYPE.UI,
236
+ attributes: {
237
+ selectedTab: selectedTabMetadata.selectedTab,
238
+ selectedTabIndex: selectedTabIndex
239
+ }
240
+ };
241
+ dispatchAnalyticsEvent(payload);
242
+ }
243
+ }, [dispatchAnalyticsEvent, getTabAnalyticsMetadata]);
85
244
  if (!isOpen || !mediaProvider) {
86
245
  return null;
87
246
  }
@@ -110,6 +269,7 @@ export var MediaInsertPicker = function MediaInsertPicker(_ref2) {
110
269
  };
111
270
  };
112
271
  var fileTabTitle = expValEqualsNoExposure('cc_page_experiences_editor_image_generation', 'isEnabled', true) ? intl.formatMessage(mediaInsertMessages.uploadTabTitle) : intl.formatMessage(mediaInsertMessages.fileTabTitle);
272
+ var linkTabIndex = isMediaInsertTabWithAnalyticsEnabled ? registeredTabs.length + (isOnlyExternalLinks ? 0 : 1) : 0;
113
273
  return /*#__PURE__*/React.createElement(PopupWithListeners, {
114
274
  ariaLabel: intl.formatMessage(mediaInsertMessages.mediaPickerPopupAriaLabel)
115
275
  // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
@@ -131,16 +291,33 @@ export var MediaInsertPicker = function MediaInsertPicker(_ref2) {
131
291
  return /*#__PURE__*/React.createElement(MediaInsertWrapper, {
132
292
  ref: setOutsideClickTargetRef
133
293
  }, /*#__PURE__*/React.createElement(Tabs, {
134
- id: "media-insert-tab-navigation"
294
+ id: "media-insert-tab-navigation",
295
+ analyticsContext: isMediaInsertTabWithAnalyticsEnabled ? tabsAnalyticsContext : undefined,
296
+ onChange: isMediaInsertTabWithAnalyticsEnabled ? handleTabChange : undefined
135
297
  }, /*#__PURE__*/React.createElement(Box, {
136
298
  paddingBlockEnd: "space.150"
137
- }, /*#__PURE__*/React.createElement(TabList, null, registeredTabs.map(function (tab) {
299
+ }, /*#__PURE__*/React.createElement(TabList, null, isMediaInsertTabWithAnalyticsEnabled && registeredTabs.map(function (tab, index) {
300
+ return /*#__PURE__*/React.createElement(AnalyticsTab, {
301
+ key: tab.key,
302
+ onSelectTabForAnalytics: setSelectedTabAnalyticsMetadata,
303
+ selectedTabIndex: index,
304
+ tabCount: tabCount
305
+ }, tab.label);
306
+ }), isMediaInsertTabWithAnalyticsEnabled && !isOnlyExternalLinks && /*#__PURE__*/React.createElement(AnalyticsTab, {
307
+ onSelectTabForAnalytics: setSelectedTabAnalyticsMetadata,
308
+ selectedTabIndex: registeredTabs.length,
309
+ tabCount: tabCount
310
+ }, fileTabTitle), isMediaInsertTabWithAnalyticsEnabled && /*#__PURE__*/React.createElement(AnalyticsTab, {
311
+ onSelectTabForAnalytics: setSelectedTabAnalyticsMetadata,
312
+ selectedTabIndex: linkTabIndex,
313
+ tabCount: tabCount
314
+ }, intl.formatMessage(mediaInsertMessages.linkTabTitle)), !isMediaInsertTabWithAnalyticsEnabled && registeredTabs.map(function (tab) {
138
315
  return /*#__PURE__*/React.createElement(Tab, {
139
316
  key: tab.key
140
317
  }, tab.label);
141
- }), !isOnlyExternalLinks && /*#__PURE__*/React.createElement(Tab, null, fileTabTitle), /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.linkTabTitle)))), registeredTabs.map(function (_ref3) {
142
- var key = _ref3.key,
143
- TabComponent = _ref3.component;
318
+ }), !isMediaInsertTabWithAnalyticsEnabled && !isOnlyExternalLinks && /*#__PURE__*/React.createElement(Tab, null, fileTabTitle), !isMediaInsertTabWithAnalyticsEnabled && /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.linkTabTitle)))), registeredTabs.map(function (_ref4) {
319
+ var key = _ref4.key,
320
+ TabComponent = _ref4.component;
144
321
  return /*#__PURE__*/React.createElement(CustomTabPanel, {
145
322
  key: key,
146
323
  disablePaddingBlockEnd: true
@@ -28,6 +28,10 @@ export type MediaInsertTabProps = {
28
28
  * needs to import them directly.
29
29
  */
30
30
  export type RegisterInsertTab = {
31
+ /**
32
+ * Component rendered inside the picker when this tab is active.
33
+ */
34
+ component: ComponentType<MediaInsertTabProps>;
31
35
  /**
32
36
  * Stable identifier for this tab. Used to de-duplicate registrations so
33
37
  * calling `registerInsertTab` with the same key twice replaces the prior
@@ -39,10 +43,6 @@ export type RegisterInsertTab = {
39
43
  * registering plugin owns its own i18n.
40
44
  */
41
45
  label: ReactNode;
42
- /**
43
- * Component rendered inside the picker when this tab is active.
44
- */
45
- component: ComponentType<MediaInsertTabProps>;
46
46
  };
47
47
  export type MediaInsertPluginState = {
48
48
  isOpen?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-media-insert",
3
- "version": "25.0.1",
3
+ "version": "25.0.3",
4
4
  "description": "Media Insert plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "atlaskit:src": "src/index.ts",
23
23
  "dependencies": {
24
- "@atlaskit/adf-schema": "^55.0.0",
24
+ "@atlaskit/adf-schema": "^55.2.0",
25
25
  "@atlaskit/button": "^24.0.0",
26
26
  "@atlaskit/editor-plugin-analytics": "^12.0.0",
27
27
  "@atlaskit/editor-plugin-media": "^14.0.0",
@@ -33,15 +33,16 @@
33
33
  "@atlaskit/media-client": "^37.0.0",
34
34
  "@atlaskit/media-client-react": "^6.0.0",
35
35
  "@atlaskit/media-picker": "^72.0.0",
36
+ "@atlaskit/platform-feature-flags": "^2.0.0",
36
37
  "@atlaskit/primitives": "^20.0.0",
37
38
  "@atlaskit/section-message": "^9.0.0",
38
39
  "@atlaskit/tabs": "^20.0.0",
39
40
  "@atlaskit/textfield": "^9.0.0",
40
- "@atlaskit/tmp-editor-statsig": "^105.0.0",
41
+ "@atlaskit/tmp-editor-statsig": "^107.1.0",
41
42
  "@babel/runtime": "^7.0.0"
42
43
  },
43
44
  "peerDependencies": {
44
- "@atlaskit/editor-common": "^116.1.0",
45
+ "@atlaskit/editor-common": "^116.3.0",
45
46
  "@atlaskit/tokens": "^14.0.0",
46
47
  "react": "^18.2.0",
47
48
  "react-dom": "^18.2.0",
@@ -92,5 +93,10 @@
92
93
  "react": "^18.2.0",
93
94
  "react-dom": "^18.2.0",
94
95
  "react-intl": "^6.6.2"
96
+ },
97
+ "platform-feature-flags": {
98
+ "media_insert_tab_with_analytics": {
99
+ "type": "boolean"
100
+ }
95
101
  }
96
102
  }