@atlaskit/editor-plugin-insert-block 8.4.4 → 8.4.5

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 +8 -0
  2. package/dist/cjs/insertBlockPlugin.js +17 -0
  3. package/dist/cjs/pm-plugins/experiences/toolbar-action-experiences.js +183 -0
  4. package/dist/cjs/pm-plugins/experiences/toolbar-experience-utils.js +409 -0
  5. package/dist/cjs/ui/toolbar-components/EmojiButton.js +3 -1
  6. package/dist/cjs/ui/toolbar-components/ImageButton.js +2 -1
  7. package/dist/cjs/ui/toolbar-components/InsertButton.js +2 -1
  8. package/dist/cjs/ui/toolbar-components/LayoutButton.js +2 -1
  9. package/dist/cjs/ui/toolbar-components/MediaButton.js +3 -1
  10. package/dist/cjs/ui/toolbar-components/MentionButton.js +3 -1
  11. package/dist/cjs/ui/toolbar-components/TableButton.js +1 -1
  12. package/dist/cjs/ui/toolbar-components/TableSizePicker.js +3 -1
  13. package/dist/cjs/ui/toolbar-components/TaskListButton.js +2 -1
  14. package/dist/es2019/insertBlockPlugin.js +15 -0
  15. package/dist/es2019/pm-plugins/experiences/toolbar-action-experiences.js +173 -0
  16. package/dist/es2019/pm-plugins/experiences/toolbar-experience-utils.js +279 -0
  17. package/dist/es2019/ui/toolbar-components/EmojiButton.js +3 -1
  18. package/dist/es2019/ui/toolbar-components/ImageButton.js +3 -2
  19. package/dist/es2019/ui/toolbar-components/InsertButton.js +3 -2
  20. package/dist/es2019/ui/toolbar-components/LayoutButton.js +3 -2
  21. package/dist/es2019/ui/toolbar-components/MediaButton.js +3 -1
  22. package/dist/es2019/ui/toolbar-components/MentionButton.js +3 -1
  23. package/dist/es2019/ui/toolbar-components/TableButton.js +2 -2
  24. package/dist/es2019/ui/toolbar-components/TableSizePicker.js +3 -1
  25. package/dist/es2019/ui/toolbar-components/TaskListButton.js +3 -2
  26. package/dist/esm/insertBlockPlugin.js +17 -0
  27. package/dist/esm/pm-plugins/experiences/toolbar-action-experiences.js +177 -0
  28. package/dist/esm/pm-plugins/experiences/toolbar-experience-utils.js +403 -0
  29. package/dist/esm/ui/toolbar-components/EmojiButton.js +3 -1
  30. package/dist/esm/ui/toolbar-components/ImageButton.js +3 -2
  31. package/dist/esm/ui/toolbar-components/InsertButton.js +3 -2
  32. package/dist/esm/ui/toolbar-components/LayoutButton.js +3 -2
  33. package/dist/esm/ui/toolbar-components/MediaButton.js +3 -1
  34. package/dist/esm/ui/toolbar-components/MentionButton.js +3 -1
  35. package/dist/esm/ui/toolbar-components/TableButton.js +2 -2
  36. package/dist/esm/ui/toolbar-components/TableSizePicker.js +3 -1
  37. package/dist/esm/ui/toolbar-components/TaskListButton.js +3 -2
  38. package/dist/types/pm-plugins/experiences/toolbar-action-experiences.d.ts +10 -0
  39. package/dist/types/pm-plugins/experiences/toolbar-experience-utils.d.ts +57 -0
  40. package/dist/types-ts4.5/pm-plugins/experiences/toolbar-action-experiences.d.ts +10 -0
  41. package/dist/types-ts4.5/pm-plugins/experiences/toolbar-experience-utils.d.ts +57 -0
  42. package/package.json +5 -1
@@ -3,7 +3,7 @@ import { useIntl } from 'react-intl-next';
3
3
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
4
  import { ToolTipContent, getAriaKeyshortcuts, toggleTable } from '@atlaskit/editor-common/keymaps';
5
5
  import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
6
- import { useEditorToolbar } from '@atlaskit/editor-common/toolbar';
6
+ import { useEditorToolbar, TOOLBAR_BUTTON_TEST_ID } from '@atlaskit/editor-common/toolbar';
7
7
  import { ToolbarButton, ToolbarTooltip, TableIcon } from '@atlaskit/editor-toolbar';
8
8
  export const TableButton = ({
9
9
  api
@@ -50,6 +50,6 @@ export const TableButton = ({
50
50
  }),
51
51
  onClick: onClick,
52
52
  ariaKeyshortcuts: getAriaKeyshortcuts(toggleTable),
53
- testId: "Table"
53
+ testId: TOOLBAR_BUTTON_TEST_ID.TABLE
54
54
  }));
55
55
  };
@@ -1,6 +1,7 @@
1
1
  import React, { useRef } from 'react';
2
2
  import { useIntl } from 'react-intl-next';
3
3
  import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
4
+ import { TOOLBAR_BUTTON_TEST_ID } from '@atlaskit/editor-common/toolbar';
4
5
  import { MoreItemsIcon, ToolbarButton, ToolbarTooltip, useToolbarUI } from '@atlaskit/editor-toolbar';
5
6
  import { useTableSelectorPopup } from './hooks/useTableSelectorPopup';
6
7
  import { TableSelectorPopupWrapper } from './popups/TableSelectorPopupWrapper';
@@ -49,6 +50,7 @@ export const TableSizePicker = ({
49
50
  }),
50
51
  onClick: onClick,
51
52
  isSelected: tableSelectorPopup.isOpen,
52
- ref: tableSizePickerRef
53
+ ref: tableSizePickerRef,
54
+ testId: TOOLBAR_BUTTON_TEST_ID.TABLE_SELECTOR
53
55
  })));
54
56
  };
@@ -3,7 +3,7 @@ import { useIntl } from 'react-intl-next';
3
3
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
4
  import { ToolTipContent, insertTaskList } from '@atlaskit/editor-common/keymaps';
5
5
  import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
6
- import { useEditorToolbar } from '@atlaskit/editor-common/toolbar';
6
+ import { TOOLBAR_BUTTON_TEST_ID, useEditorToolbar } from '@atlaskit/editor-common/toolbar';
7
7
  import { ToolbarButton, ToolbarTooltip, TaskIcon } from '@atlaskit/editor-toolbar';
8
8
  export const TaskListButton = ({
9
9
  api
@@ -38,6 +38,7 @@ export const TaskListButton = ({
38
38
  size: "small"
39
39
  }),
40
40
  onClick: onClick,
41
- ariaKeyshortcuts: "[ ] Space"
41
+ ariaKeyshortcuts: "[ ] Space",
42
+ testId: TOOLBAR_BUTTON_TEST_ID.TASK_LIST
42
43
  }));
43
44
  };
@@ -9,6 +9,7 @@ import { BLOCK_QUOTE, CODE_BLOCK, PANEL } from '@atlaskit/editor-plugin-block-ty
9
9
  import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
10
10
  import { fg } from '@atlaskit/platform-feature-flags';
11
11
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
12
+ import { getToolbarActionExperiencesPlugin } from './pm-plugins/experiences/toolbar-action-experiences';
12
13
  import { toggleInsertBlockPmKey, toggleInsertBlockPmPlugin } from './pm-plugins/toggleInsertBlock';
13
14
  import { getToolbarComponents } from './ui/toolbar-components';
14
15
  // Ignored via go/ees005
@@ -98,6 +99,7 @@ export var insertBlockPlugin = function insertBlockPlugin(_ref) {
98
99
  options = _ref$config === void 0 ? {} : _ref$config,
99
100
  api = _ref.api;
100
101
  var isToolbarAIFCEnabled = Boolean(api === null || api === void 0 ? void 0 : api.toolbar);
102
+ var refs = {};
101
103
  var primaryToolbarComponent = function primaryToolbarComponent(_ref2) {
102
104
  var editorView = _ref2.editorView,
103
105
  editorActions = _ref2.editorActions,
@@ -110,6 +112,7 @@ export var insertBlockPlugin = function insertBlockPlugin(_ref) {
110
112
  disabled = _ref2.disabled,
111
113
  isToolbarReducedSpacing = _ref2.isToolbarReducedSpacing,
112
114
  isLastItem = _ref2.isLastItem;
115
+ refs.popupsMountPoint = popupsMountPoint || undefined;
113
116
  var renderNode = function renderNode(providers) {
114
117
  if (!editorView) {
115
118
  return null;
@@ -216,6 +219,20 @@ export var insertBlockPlugin = function insertBlockPlugin(_ref) {
216
219
  return toggleInsertBlockPmPlugin();
217
220
  }
218
221
  });
222
+ if (fg('platform_editor_experience_tracking_toolbar_button')) {
223
+ plugins.push({
224
+ name: 'toolbarActionExperiences',
225
+ plugin: function plugin() {
226
+ return getToolbarActionExperiencesPlugin({
227
+ refs: refs,
228
+ dispatchAnalyticsEvent: function dispatchAnalyticsEvent(payload) {
229
+ var _api$analytics;
230
+ return api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 ? void 0 : _api$analytics.fireAnalyticsEvent(payload);
231
+ }
232
+ });
233
+ }
234
+ });
235
+ }
219
236
  return plugins;
220
237
  },
221
238
  pluginsOptions: {},
@@ -0,0 +1,177 @@
1
+ import { bind } from 'bind-event-listener';
2
+ import { getDocument } from '@atlaskit/browser-apis';
3
+ import { Experience, EXPERIENCE_ID, ExperienceCheckDomMutation, ExperienceCheckTimeout, getPopupContainerFromEditorView } from '@atlaskit/editor-common/experiences';
4
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
5
+ import { TOOLBAR_BUTTON_TEST_ID } from '@atlaskit/editor-common/toolbar';
6
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
7
+ import { ExperienceCheckPopupMutation, getParentDOMAtSelection, handleEditorNodeInsertDomMutation, handleTypeAheadOpenDomMutation, isToolbarButtonClick } from './toolbar-experience-utils';
8
+ var pluginKey = new PluginKey('toolbarActionExperiences');
9
+ var TIMEOUT_DURATION = 1000;
10
+ var PRIMARY_TOOLBAR = 'primaryToolbar';
11
+ var ABORT_REASON = {
12
+ USER_CANCELED: 'userCanceled',
13
+ EDITOR_DESTROYED: 'editorDestroyed'
14
+ };
15
+ export var getToolbarActionExperiencesPlugin = function getToolbarActionExperiencesPlugin(_ref) {
16
+ var refs = _ref.refs,
17
+ dispatchAnalyticsEvent = _ref.dispatchAnalyticsEvent;
18
+ var editorView;
19
+ var popupTargetEl;
20
+ var getPopupsTarget = function getPopupsTarget() {
21
+ if (!popupTargetEl) {
22
+ var _editorView;
23
+ popupTargetEl = refs.popupsMountPoint || getPopupContainerFromEditorView((_editorView = editorView) === null || _editorView === void 0 ? void 0 : _editorView.dom);
24
+ }
25
+ return popupTargetEl;
26
+ };
27
+ var getEditorDom = function getEditorDom() {
28
+ var _editorView2;
29
+ if (((_editorView2 = editorView) === null || _editorView2 === void 0 ? void 0 : _editorView2.dom) instanceof HTMLElement) {
30
+ return editorView.dom;
31
+ }
32
+ return null;
33
+ };
34
+ var narrowParentObserveConfig = function narrowParentObserveConfig() {
35
+ var _getParentDOMAtSelect;
36
+ return {
37
+ target: (_getParentDOMAtSelect = getParentDOMAtSelection(editorView)) !== null && _getParentDOMAtSelect !== void 0 ? _getParentDOMAtSelect : getEditorDom(),
38
+ options: {
39
+ childList: true
40
+ }
41
+ };
42
+ };
43
+ var rootObserveConfig = function rootObserveConfig() {
44
+ return {
45
+ target: getEditorDom(),
46
+ options: {
47
+ childList: true
48
+ }
49
+ };
50
+ };
51
+ var createNodeInsertExperience = function createNodeInsertExperience(action) {
52
+ return new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
53
+ action: action,
54
+ actionSubjectId: PRIMARY_TOOLBAR,
55
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
56
+ checks: [new ExperienceCheckTimeout({
57
+ durationMs: TIMEOUT_DURATION
58
+ }), new ExperienceCheckDomMutation({
59
+ onDomMutation: handleEditorNodeInsertDomMutation,
60
+ observeConfig: narrowParentObserveConfig
61
+ }), new ExperienceCheckDomMutation({
62
+ onDomMutation: handleEditorNodeInsertDomMutation,
63
+ observeConfig: rootObserveConfig
64
+ })]
65
+ });
66
+ };
67
+ var createPopupExperience = function createPopupExperience(action, popupSelector) {
68
+ return new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
69
+ action: action,
70
+ actionSubjectId: PRIMARY_TOOLBAR,
71
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
72
+ checks: [new ExperienceCheckTimeout({
73
+ durationMs: TIMEOUT_DURATION
74
+ }), new ExperienceCheckPopupMutation(popupSelector, getPopupsTarget, getEditorDom)]
75
+ });
76
+ };
77
+ var experienceButtonMappings = [{
78
+ experience: createPopupExperience('emoji', '[data-emoji-picker-container]'),
79
+ buttonTestId: TOOLBAR_BUTTON_TEST_ID.EMOJI
80
+ }, {
81
+ experience: createPopupExperience('media', '[id="local-media-upload-button"], [data-testid="media-picker-file-input"]'),
82
+ buttonTestId: TOOLBAR_BUTTON_TEST_ID.MEDIA
83
+ }, {
84
+ experience: new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
85
+ action: 'mention',
86
+ actionSubjectId: PRIMARY_TOOLBAR,
87
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
88
+ checks: [new ExperienceCheckTimeout({
89
+ durationMs: TIMEOUT_DURATION
90
+ }), new ExperienceCheckDomMutation({
91
+ onDomMutation: handleTypeAheadOpenDomMutation,
92
+ observeConfig: narrowParentObserveConfig
93
+ })]
94
+ }),
95
+ buttonTestId: TOOLBAR_BUTTON_TEST_ID.MENTION
96
+ }, {
97
+ experience: createNodeInsertExperience('table'),
98
+ buttonTestId: TOOLBAR_BUTTON_TEST_ID.TABLE
99
+ }, {
100
+ experience: createPopupExperience('tableSelector', '[aria-label*="table size"], [data-testid*="table-selector"]'),
101
+ buttonTestId: TOOLBAR_BUTTON_TEST_ID.TABLE_SELECTOR
102
+ }, {
103
+ experience: createNodeInsertExperience('layout'),
104
+ buttonTestId: TOOLBAR_BUTTON_TEST_ID.LAYOUT
105
+ }, {
106
+ experience: createPopupExperience('image', '[id="local-media-upload-button"], [data-testid="media-picker-file-input"]'),
107
+ buttonTestId: TOOLBAR_BUTTON_TEST_ID.IMAGE
108
+ }, {
109
+ experience: createNodeInsertExperience('action'),
110
+ buttonTestId: TOOLBAR_BUTTON_TEST_ID.TASK_LIST
111
+ }];
112
+ var handleToolbarButtonClick = function handleToolbarButtonClick(target) {
113
+ for (var _i = 0, _experienceButtonMapp = experienceButtonMappings; _i < _experienceButtonMapp.length; _i++) {
114
+ var _experienceButtonMapp2 = _experienceButtonMapp[_i],
115
+ experience = _experienceButtonMapp2.experience,
116
+ buttonTestId = _experienceButtonMapp2.buttonTestId;
117
+ if (isToolbarButtonClick(target, buttonTestId)) {
118
+ experience.start({
119
+ forceRestart: true
120
+ });
121
+ return;
122
+ }
123
+ }
124
+ };
125
+ var abortAllExperiences = function abortAllExperiences(reason) {
126
+ for (var _i2 = 0, _experienceButtonMapp3 = experienceButtonMappings; _i2 < _experienceButtonMapp3.length; _i2++) {
127
+ var experience = _experienceButtonMapp3[_i2].experience;
128
+ experience.abort({
129
+ reason: reason
130
+ });
131
+ }
132
+ };
133
+ var doc = getDocument();
134
+ if (!doc) {
135
+ return new SafePlugin({
136
+ key: pluginKey
137
+ });
138
+ }
139
+ var unbindClickListener = bind(doc, {
140
+ type: 'click',
141
+ listener: function listener(event) {
142
+ var target = event.target;
143
+ if (target instanceof HTMLElement) {
144
+ handleToolbarButtonClick(target);
145
+ }
146
+ },
147
+ options: {
148
+ capture: true
149
+ }
150
+ });
151
+ var unbindKeydownListener = bind(doc, {
152
+ type: 'keydown',
153
+ listener: function listener(event) {
154
+ if (event.key === 'Escape') {
155
+ abortAllExperiences(ABORT_REASON.USER_CANCELED);
156
+ }
157
+ },
158
+ options: {
159
+ capture: true
160
+ }
161
+ });
162
+ return new SafePlugin({
163
+ key: pluginKey,
164
+ view: function view(_view) {
165
+ editorView = _view;
166
+ return {
167
+ destroy: function destroy() {
168
+ abortAllExperiences(ABORT_REASON.EDITOR_DESTROYED);
169
+ editorView = undefined;
170
+ popupTargetEl = undefined;
171
+ unbindClickListener();
172
+ unbindKeydownListener();
173
+ }
174
+ };
175
+ }
176
+ });
177
+ };
@@ -0,0 +1,403 @@
1
+ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
2
+ import _createClass from "@babel/runtime/helpers/createClass";
3
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
5
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
6
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
7
+ import { getDocument } from '@atlaskit/browser-apis';
8
+ import { EXPERIENCE_FAILURE_REASON, popupWithNestedElement } from '@atlaskit/editor-common/experiences';
9
+ /**
10
+ * DOM marker selectors for node types inserted via toolbar actions.
11
+ * Matches outermost wrapper elements set synchronously by ReactNodeView
12
+ * (`{nodeTypeName}View-content-wrap`) or schema `toDOM` attributes.
13
+ */
14
+ export var NODE_INSERT_MARKERS = {
15
+ TABLE: '.tableView-content-wrap',
16
+ LAYOUT: '.layoutSectionView-content-wrap',
17
+ LAYOUT_COLUMN: '.layoutColumnView-content-wrap',
18
+ TASK_LIST: '[data-node-type="actionList"]',
19
+ TASK_ITEM: '.taskItemView-content-wrap'
20
+ };
21
+ var COMBINED_NODE_INSERT_SELECTOR = [NODE_INSERT_MARKERS.TABLE, NODE_INSERT_MARKERS.LAYOUT, NODE_INSERT_MARKERS.LAYOUT_COLUMN, NODE_INSERT_MARKERS.TASK_LIST, NODE_INSERT_MARKERS.TASK_ITEM].join(', ');
22
+ export var isToolbarButtonClick = function isToolbarButtonClick(target, testId) {
23
+ var button = target.closest("button[data-testid=\"".concat(testId, "\"]"));
24
+ if (!button) {
25
+ return false;
26
+ }
27
+ return !button.disabled && button.getAttribute('aria-disabled') !== 'true';
28
+ };
29
+
30
+ /**
31
+ * ExperienceCheck that observes popup mount point and all its
32
+ * `[data-editor-popup]` children with `{ childList: true }` (no subtree).
33
+ *
34
+ * Detects when a popup containing the given nested element is added to the
35
+ * DOM — either as a new `[data-editor-popup]` direct child, or as content
36
+ * rendered inside an existing `[data-editor-popup]` wrapper.
37
+ */
38
+ export var TYPEAHEAD_DECORATION_SELECTOR = '[data-type-ahead="typeaheadDecoration"]';
39
+ export var handleTypeAheadOpenDomMutation = function handleTypeAheadOpenDomMutation(_ref) {
40
+ var mutations = _ref.mutations;
41
+ var _iterator = _createForOfIteratorHelper(mutations),
42
+ _step;
43
+ try {
44
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
45
+ var mutation = _step.value;
46
+ if (mutation.type !== 'childList') {
47
+ continue;
48
+ }
49
+ var _iterator2 = _createForOfIteratorHelper(mutation.addedNodes),
50
+ _step2;
51
+ try {
52
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
53
+ var node = _step2.value;
54
+ if (!(node instanceof HTMLElement)) {
55
+ continue;
56
+ }
57
+ if (node.matches(TYPEAHEAD_DECORATION_SELECTOR) || node.querySelector(TYPEAHEAD_DECORATION_SELECTOR)) {
58
+ return {
59
+ status: 'success'
60
+ };
61
+ }
62
+ }
63
+ } catch (err) {
64
+ _iterator2.e(err);
65
+ } finally {
66
+ _iterator2.f();
67
+ }
68
+ }
69
+ } catch (err) {
70
+ _iterator.e(err);
71
+ } finally {
72
+ _iterator.f();
73
+ }
74
+ return undefined;
75
+ };
76
+ export var ExperienceCheckPopupMutation = /*#__PURE__*/function () {
77
+ function ExperienceCheckPopupMutation(nestedElementQuery, getTarget, getEditorDom) {
78
+ _classCallCheck(this, ExperienceCheckPopupMutation);
79
+ _defineProperty(this, "observers", []);
80
+ this.nestedElementQuery = nestedElementQuery;
81
+ this.getTarget = getTarget;
82
+ this.getEditorDom = getEditorDom;
83
+ }
84
+ return _createClass(ExperienceCheckPopupMutation, [{
85
+ key: "start",
86
+ value: function start(callback) {
87
+ var _this = this;
88
+ this.stop();
89
+ var target = this.getTarget();
90
+ if (!target) {
91
+ callback({
92
+ status: 'failure',
93
+ reason: EXPERIENCE_FAILURE_REASON.DOM_MUTATION_TARGET_NOT_FOUND
94
+ });
95
+ return;
96
+ }
97
+ var doc = getDocument();
98
+ if (!doc) {
99
+ callback({
100
+ status: 'failure',
101
+ reason: EXPERIENCE_FAILURE_REASON.DOM_MUTATION_TARGET_NOT_FOUND
102
+ });
103
+ return;
104
+ }
105
+ var query = this.nestedElementQuery;
106
+ var onMutation = function onMutation(mutations) {
107
+ var _iterator3 = _createForOfIteratorHelper(mutations),
108
+ _step3;
109
+ try {
110
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
111
+ var mutation = _step3.value;
112
+ if (mutation.type !== 'childList') {
113
+ continue;
114
+ }
115
+ var _iterator4 = _createForOfIteratorHelper(mutation.addedNodes),
116
+ _step4;
117
+ try {
118
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
119
+ var node = _step4.value;
120
+ if (!(node instanceof HTMLElement)) {
121
+ continue;
122
+ }
123
+ if (popupWithNestedElement(node, query) || node.matches(query) || !!node.querySelector(query)) {
124
+ _this.stop();
125
+ callback({
126
+ status: 'success'
127
+ });
128
+ return;
129
+ }
130
+ }
131
+ } catch (err) {
132
+ _iterator4.e(err);
133
+ } finally {
134
+ _iterator4.f();
135
+ }
136
+ }
137
+ } catch (err) {
138
+ _iterator3.e(err);
139
+ } finally {
140
+ _iterator3.f();
141
+ }
142
+ };
143
+ var observe = function observe(el) {
144
+ var observer = new MutationObserver(onMutation);
145
+ observer.observe(el, {
146
+ childList: true
147
+ });
148
+ _this.observers.push(observer);
149
+ };
150
+ observe(target);
151
+ var _iterator5 = _createForOfIteratorHelper(target.querySelectorAll('[data-editor-popup]')),
152
+ _step5;
153
+ try {
154
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
155
+ var wrapper = _step5.value;
156
+ observe(wrapper);
157
+ }
158
+ } catch (err) {
159
+ _iterator5.e(err);
160
+ } finally {
161
+ _iterator5.f();
162
+ }
163
+ var portalContainer = doc.querySelector('.atlaskit-portal-container');
164
+ if (portalContainer instanceof HTMLElement) {
165
+ var observePortal = function observePortal(portal) {
166
+ observe(portal);
167
+ var _iterator6 = _createForOfIteratorHelper(portal.children),
168
+ _step6;
169
+ try {
170
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
171
+ var child = _step6.value;
172
+ if (child instanceof HTMLElement) {
173
+ observe(child);
174
+ }
175
+ }
176
+ } catch (err) {
177
+ _iterator6.e(err);
178
+ } finally {
179
+ _iterator6.f();
180
+ }
181
+ };
182
+ var containerObserver = new MutationObserver(function (mutations) {
183
+ var _iterator7 = _createForOfIteratorHelper(mutations),
184
+ _step7;
185
+ try {
186
+ for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
187
+ var mutation = _step7.value;
188
+ if (mutation.type !== 'childList') {
189
+ continue;
190
+ }
191
+ var _iterator8 = _createForOfIteratorHelper(mutation.addedNodes),
192
+ _step8;
193
+ try {
194
+ for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
195
+ var node = _step8.value;
196
+ if (node instanceof HTMLElement) {
197
+ observePortal(node);
198
+ }
199
+ }
200
+ } catch (err) {
201
+ _iterator8.e(err);
202
+ } finally {
203
+ _iterator8.f();
204
+ }
205
+ }
206
+ } catch (err) {
207
+ _iterator7.e(err);
208
+ } finally {
209
+ _iterator7.f();
210
+ }
211
+ onMutation(mutations);
212
+ });
213
+ containerObserver.observe(portalContainer, {
214
+ childList: true
215
+ });
216
+ this.observers.push(containerObserver);
217
+ var _iterator9 = _createForOfIteratorHelper(portalContainer.querySelectorAll('.atlaskit-portal')),
218
+ _step9;
219
+ try {
220
+ for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
221
+ var portal = _step9.value;
222
+ observePortal(portal);
223
+ }
224
+ } catch (err) {
225
+ _iterator9.e(err);
226
+ } finally {
227
+ _iterator9.f();
228
+ }
229
+ }
230
+ var editorDom = this.getEditorDom();
231
+ if (editorDom !== null && editorDom !== void 0 && editorDom.parentElement) {
232
+ observe(editorDom.parentElement);
233
+ }
234
+
235
+ // Two-frame DOM check to handle cases where rendering happens before
236
+ // observers are attached.
237
+ var checkDom = function checkDom() {
238
+ if (doc.querySelector(query)) {
239
+ _this.stop();
240
+ callback({
241
+ status: 'success'
242
+ });
243
+ return;
244
+ }
245
+ requestAnimationFrame(function () {
246
+ if (doc.querySelector(query)) {
247
+ _this.stop();
248
+ callback({
249
+ status: 'success'
250
+ });
251
+ }
252
+ });
253
+ };
254
+ requestAnimationFrame(checkDom);
255
+ }
256
+ }, {
257
+ key: "stop",
258
+ value: function stop() {
259
+ var _iterator0 = _createForOfIteratorHelper(this.observers),
260
+ _step0;
261
+ try {
262
+ for (_iterator0.s(); !(_step0 = _iterator0.n()).done;) {
263
+ var observer = _step0.value;
264
+ observer.disconnect();
265
+ }
266
+ } catch (err) {
267
+ _iterator0.e(err);
268
+ } finally {
269
+ _iterator0.f();
270
+ }
271
+ this.observers = [];
272
+ }
273
+ }]);
274
+ }();
275
+
276
+ /**
277
+ * Returns the narrow parent DOM element at the current selection, suitable
278
+ * for observing with `{ childList: true }` (no subtree).
279
+ *
280
+ * Uses the resolved position's depth to find the block node at the cursor
281
+ * via `nodeDOM`, then returns its `parentElement` — the container whose
282
+ * direct children change when content is inserted at this position.
283
+ *
284
+ * Falls back to `domAtPos` if `nodeDOM` is unavailable.
285
+ */
286
+ export var getParentDOMAtSelection = function getParentDOMAtSelection(editorView) {
287
+ if (!editorView) {
288
+ return null;
289
+ }
290
+ try {
291
+ var selection = editorView.state.selection;
292
+ var $from = selection.$from;
293
+ var parentDepth = Math.max(1, $from.depth);
294
+ var parentPos = $from.before(parentDepth);
295
+ var parentDom = editorView.nodeDOM(parentPos);
296
+ if (parentDom instanceof HTMLElement && parentDom.parentElement) {
297
+ return parentDom.parentElement;
298
+ }
299
+
300
+ // Fallback: use domAtPos
301
+ var _editorView$domAtPos = editorView.domAtPos(selection.from),
302
+ node = _editorView$domAtPos.node;
303
+ var element = null;
304
+ if (node instanceof HTMLElement) {
305
+ element = node;
306
+ } else if (node instanceof Text) {
307
+ element = node.parentElement;
308
+ }
309
+ if (!element) {
310
+ return null;
311
+ }
312
+ var proseMirrorRoot = editorView.dom;
313
+ if (!(proseMirrorRoot instanceof HTMLElement)) {
314
+ return null;
315
+ }
316
+ if (element === proseMirrorRoot) {
317
+ return proseMirrorRoot;
318
+ }
319
+ if (element.parentElement && proseMirrorRoot.contains(element.parentElement)) {
320
+ return element.parentElement;
321
+ }
322
+ return proseMirrorRoot;
323
+ } catch (_unused) {
324
+ return null;
325
+ }
326
+ };
327
+
328
+ /**
329
+ * Checks whether a DOM node matches any known node insert marker,
330
+ * either directly or via a nested element (e.g. breakout mark wrapper).
331
+ */
332
+ var matchesNodeInsertMarker = function matchesNodeInsertMarker(node) {
333
+ if (!(node instanceof HTMLElement)) {
334
+ return false;
335
+ }
336
+ return node.matches(COMBINED_NODE_INSERT_SELECTOR) || !!node.querySelector(COMBINED_NODE_INSERT_SELECTOR);
337
+ };
338
+
339
+ /**
340
+ * Evaluates DOM mutations to detect a node insert action.
341
+ *
342
+ * Uses two strategies:
343
+ * 1. Marker-based: checks `addedNodes` against known node insert selectors.
344
+ * 2. Structure-based: detects element add+remove (block-level replacement).
345
+ */
346
+ export var handleEditorNodeInsertDomMutation = function handleEditorNodeInsertDomMutation(_ref2) {
347
+ var mutations = _ref2.mutations;
348
+ var hasAddedElement = false;
349
+ var hasRemovedElement = false;
350
+ var _iterator1 = _createForOfIteratorHelper(mutations),
351
+ _step1;
352
+ try {
353
+ for (_iterator1.s(); !(_step1 = _iterator1.n()).done;) {
354
+ var mutation = _step1.value;
355
+ if (mutation.type !== 'childList') {
356
+ continue;
357
+ }
358
+ var _iterator10 = _createForOfIteratorHelper(mutation.addedNodes),
359
+ _step10;
360
+ try {
361
+ for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
362
+ var node = _step10.value;
363
+ if (matchesNodeInsertMarker(node)) {
364
+ return {
365
+ status: 'success'
366
+ };
367
+ }
368
+ if (node instanceof HTMLElement) {
369
+ hasAddedElement = true;
370
+ }
371
+ }
372
+ } catch (err) {
373
+ _iterator10.e(err);
374
+ } finally {
375
+ _iterator10.f();
376
+ }
377
+ var _iterator11 = _createForOfIteratorHelper(mutation.removedNodes),
378
+ _step11;
379
+ try {
380
+ for (_iterator11.s(); !(_step11 = _iterator11.n()).done;) {
381
+ var _node = _step11.value;
382
+ if (_node instanceof HTMLElement) {
383
+ hasRemovedElement = true;
384
+ }
385
+ }
386
+ } catch (err) {
387
+ _iterator11.e(err);
388
+ } finally {
389
+ _iterator11.f();
390
+ }
391
+ }
392
+ } catch (err) {
393
+ _iterator1.e(err);
394
+ } finally {
395
+ _iterator1.f();
396
+ }
397
+ if (hasAddedElement && hasRemovedElement) {
398
+ return {
399
+ status: 'success'
400
+ };
401
+ }
402
+ return undefined;
403
+ };
@@ -3,6 +3,7 @@ import { useIntl } from 'react-intl-next';
3
3
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
4
4
  import { ToolTipContent, insertEmoji } from '@atlaskit/editor-common/keymaps';
5
5
  import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
6
+ import { TOOLBAR_BUTTON_TEST_ID } from '@atlaskit/editor-common/toolbar';
6
7
  import { ToolbarButton, ToolbarTooltip, EmojiIcon, useToolbarUI } from '@atlaskit/editor-toolbar';
7
8
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
8
9
  import { useEmojiPickerPopup } from './hooks/useEmojiPickerPopup';
@@ -67,6 +68,7 @@ export var EmojiButton = function EmojiButton(_ref) {
67
68
  return emojiPickerPopup.toggle();
68
69
  },
69
70
  isSelected: emojiPickerPopup.isOpen,
70
- isDisabled: !isTypeAheadAllowed || !emojiProvider
71
+ isDisabled: !isTypeAheadAllowed || !emojiProvider,
72
+ testId: TOOLBAR_BUTTON_TEST_ID.EMOJI
71
73
  })));
72
74
  };