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

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 (47) hide show
  1. package/CHANGELOG.md +19 -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/ElementBrowser/InsertMenu.js +18 -5
  6. package/dist/cjs/ui/toolbar-components/EmojiButton.js +3 -1
  7. package/dist/cjs/ui/toolbar-components/ImageButton.js +2 -1
  8. package/dist/cjs/ui/toolbar-components/InsertButton.js +2 -1
  9. package/dist/cjs/ui/toolbar-components/LayoutButton.js +2 -1
  10. package/dist/cjs/ui/toolbar-components/MediaButton.js +3 -1
  11. package/dist/cjs/ui/toolbar-components/MentionButton.js +3 -1
  12. package/dist/cjs/ui/toolbar-components/TableButton.js +1 -1
  13. package/dist/cjs/ui/toolbar-components/TableSizePicker.js +3 -1
  14. package/dist/cjs/ui/toolbar-components/TaskListButton.js +2 -1
  15. package/dist/es2019/insertBlockPlugin.js +15 -0
  16. package/dist/es2019/pm-plugins/experiences/toolbar-action-experiences.js +173 -0
  17. package/dist/es2019/pm-plugins/experiences/toolbar-experience-utils.js +279 -0
  18. package/dist/es2019/ui/ElementBrowser/InsertMenu.js +15 -4
  19. package/dist/es2019/ui/toolbar-components/EmojiButton.js +3 -1
  20. package/dist/es2019/ui/toolbar-components/ImageButton.js +3 -2
  21. package/dist/es2019/ui/toolbar-components/InsertButton.js +3 -2
  22. package/dist/es2019/ui/toolbar-components/LayoutButton.js +3 -2
  23. package/dist/es2019/ui/toolbar-components/MediaButton.js +3 -1
  24. package/dist/es2019/ui/toolbar-components/MentionButton.js +3 -1
  25. package/dist/es2019/ui/toolbar-components/TableButton.js +2 -2
  26. package/dist/es2019/ui/toolbar-components/TableSizePicker.js +3 -1
  27. package/dist/es2019/ui/toolbar-components/TaskListButton.js +3 -2
  28. package/dist/esm/insertBlockPlugin.js +17 -0
  29. package/dist/esm/pm-plugins/experiences/toolbar-action-experiences.js +177 -0
  30. package/dist/esm/pm-plugins/experiences/toolbar-experience-utils.js +403 -0
  31. package/dist/esm/ui/ElementBrowser/InsertMenu.js +17 -4
  32. package/dist/esm/ui/toolbar-components/EmojiButton.js +3 -1
  33. package/dist/esm/ui/toolbar-components/ImageButton.js +3 -2
  34. package/dist/esm/ui/toolbar-components/InsertButton.js +3 -2
  35. package/dist/esm/ui/toolbar-components/LayoutButton.js +3 -2
  36. package/dist/esm/ui/toolbar-components/MediaButton.js +3 -1
  37. package/dist/esm/ui/toolbar-components/MentionButton.js +3 -1
  38. package/dist/esm/ui/toolbar-components/TableButton.js +2 -2
  39. package/dist/esm/ui/toolbar-components/TableSizePicker.js +3 -1
  40. package/dist/esm/ui/toolbar-components/TaskListButton.js +3 -2
  41. package/dist/types/pm-plugins/experiences/toolbar-action-experiences.d.ts +10 -0
  42. package/dist/types/pm-plugins/experiences/toolbar-experience-utils.d.ts +57 -0
  43. package/dist/types/ui/ElementBrowser/InsertMenu.d.ts +5 -2
  44. package/dist/types-ts4.5/pm-plugins/experiences/toolbar-action-experiences.d.ts +10 -0
  45. package/dist/types-ts4.5/pm-plugins/experiences/toolbar-experience-utils.d.ts +57 -0
  46. package/dist/types-ts4.5/ui/ElementBrowser/InsertMenu.d.ts +5 -2
  47. package/package.json +6 -2
@@ -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
+ };
@@ -24,15 +24,28 @@ import { messages, IconCode, IconDate, IconDecision, IconDivider, IconExpand, Ic
24
24
  import { OutsideClickTargetRefContext, withReactEditorViewOuterListeners as withOuterListeners } from '@atlaskit/editor-common/ui-react';
25
25
  import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
26
26
  import { N0, N30A, N60A } from '@atlaskit/theme/colors';
27
+ import { expVal } from '@atlaskit/tmp-editor-statsig/expVal';
27
28
  export var DEFAULT_HEIGHT = 560;
28
29
 
29
30
  /**
30
- * Exported helper to allow testing of InsertMenu whiteboard pinning logic. NOTE: this is
31
+ * Exported helper to allow testing of InsertMenu pinning logic. NOTE: this is
31
32
  *not* the ideal way to approach this, quickinsert plugin provides a `getSuggestions`
32
33
  method that can be used to get suggestions -> once all experiments are cleaned up,
33
34
  they should be unified through `pluginInjectionApi?.quickInsert?.actions.getSuggestions`
35
+
36
+ `cc_fd_db_top_editor_toolbar` experiment adds new logic to sort elements by `priority`
37
+ this newer implementation matches how the "quick insert menu" sorts elements
34
38
  */
35
- export var filterForPinWhiteboards = function filterForPinWhiteboards(featuredItems, formatMessage) {
39
+ export var sortPrioritizedElements = function sortPrioritizedElements(featuredItems, formatMessage) {
40
+ if (['new-description', 'orig-description'].includes(expVal('cc_fd_db_top_editor_toolbar', 'cohort', 'control'))) {
41
+ // Sort by priority (lower first) on the concatenated list so items
42
+ // with "priority" are at the top (e.g. Whiteboard before Database)
43
+ return featuredItems.slice(0).sort(function (a, b) {
44
+ return (a.priority || Number.POSITIVE_INFINITY) - (b.priority || Number.POSITIVE_INFINITY);
45
+ });
46
+ }
47
+
48
+ // old logic sort whiteboards to top
36
49
  var DIAGRAM_KEY = 'whiteboard-extension:create-diagram';
37
50
  var isDiagram = function isDiagram(item) {
38
51
  return item.key === DIAGRAM_KEY;
@@ -167,8 +180,8 @@ var InsertMenu = function InsertMenu(_ref) {
167
180
  }) : item;
168
181
  })) !== null && _pluginInjectionApi$q4 !== void 0 ? _pluginInjectionApi$q4 : [];
169
182
  var unfilteredResult = quickInsertDropdownItems.concat(featuredQuickInsertSuggestions);
170
- // need to filter on the concatenated list so whiteboards are at the top
171
- result = filterForPinWhiteboards(unfilteredResult, formatMessage);
183
+ // need to sort on the concatenated list so desired elements are at the top
184
+ result = sortPrioritizedElements(unfilteredResult, formatMessage);
172
185
  }
173
186
  setItemCount(result.length);
174
187
  return result;
@@ -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
  };
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { useIntl } from 'react-intl-next';
3
3
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
4
4
  import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
5
- import { useEditorToolbar } from '@atlaskit/editor-common/toolbar';
5
+ import { useEditorToolbar, TOOLBAR_BUTTON_TEST_ID } from '@atlaskit/editor-common/toolbar';
6
6
  import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
7
7
  import { ToolbarButton, ToolbarTooltip, ImageIcon } from '@atlaskit/editor-toolbar';
8
8
  export var ImageButton = function ImageButton(_ref) {
@@ -37,6 +37,7 @@ export var ImageButton = function ImageButton(_ref) {
37
37
  size: "small"
38
38
  }),
39
39
  onClick: onClick,
40
- isDisabled: !imageUploadEnabled || isOffline
40
+ isDisabled: !imageUploadEnabled || isOffline,
41
+ testId: TOOLBAR_BUTTON_TEST_ID.IMAGE
41
42
  }));
42
43
  };
@@ -4,7 +4,7 @@ import { useIntl } from 'react-intl-next';
4
4
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
5
5
  import { getAriaKeyshortcuts, insertElements, ToolTipContent } from '@atlaskit/editor-common/keymaps';
6
6
  import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
7
- import { useEditorToolbar } from '@atlaskit/editor-common/toolbar';
7
+ import { TOOLBAR_BUTTON_TEST_ID, useEditorToolbar } from '@atlaskit/editor-common/toolbar';
8
8
  import { Popup } from '@atlaskit/editor-common/ui';
9
9
  import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
10
10
  import { akEditorMenuZIndex } from '@atlaskit/editor-shared-styles';
@@ -241,6 +241,7 @@ export var InsertButton = function InsertButton(_ref) {
241
241
  ref: insertButtonRef,
242
242
  onClick: onClick,
243
243
  isSelected: insertMenuOpen,
244
- isDisabled: !isTypeAheadAllowed || isDisabled
244
+ isDisabled: !isTypeAheadAllowed || isDisabled,
245
+ testId: TOOLBAR_BUTTON_TEST_ID.INSERT
245
246
  })));
246
247
  };
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { useIntl } from 'react-intl-next';
3
3
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
4
  import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
5
- import { useEditorToolbar } from '@atlaskit/editor-common/toolbar';
5
+ import { useEditorToolbar, TOOLBAR_BUTTON_TEST_ID } from '@atlaskit/editor-common/toolbar';
6
6
  import { ToolbarButton, ToolbarTooltip, LayoutIcon } from '@atlaskit/editor-toolbar';
7
7
  export var LayoutButton = function LayoutButton(_ref) {
8
8
  var api = _ref.api;
@@ -28,6 +28,7 @@ export var LayoutButton = function LayoutButton(_ref) {
28
28
  label: formatMessage(messages.columns),
29
29
  size: "small"
30
30
  }),
31
- onClick: onClick
31
+ onClick: onClick,
32
+ testId: TOOLBAR_BUTTON_TEST_ID.LAYOUT
32
33
  }));
33
34
  };
@@ -3,6 +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 { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
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 { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
7
8
  import { ToolbarButton, ToolbarTooltip, ImageIcon } from '@atlaskit/editor-toolbar';
8
9
  export var MediaButton = function MediaButton(_ref) {
@@ -59,6 +60,7 @@ export var MediaButton = function MediaButton(_ref) {
59
60
  }),
60
61
  onClick: onClick,
61
62
  ref: mediaButtonRef,
62
- isDisabled: isOffline || !allowsUploads
63
+ isDisabled: isOffline || !allowsUploads,
64
+ testId: TOOLBAR_BUTTON_TEST_ID.MEDIA
63
65
  }));
64
66
  };
@@ -4,6 +4,7 @@ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
4
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
5
5
  import { ToolTipContent, insertMention } from '@atlaskit/editor-common/keymaps';
6
6
  import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
7
+ import { TOOLBAR_BUTTON_TEST_ID } from '@atlaskit/editor-common/toolbar';
7
8
  import { ToolbarButton, ToolbarTooltip, MentionIcon } from '@atlaskit/editor-toolbar';
8
9
  export var MentionButton = function MentionButton(_ref) {
9
10
  var api = _ref.api;
@@ -39,6 +40,7 @@ export var MentionButton = function MentionButton(_ref) {
39
40
  }),
40
41
  onClick: onClick,
41
42
  ariaKeyshortcuts: "Shift+2 Space",
42
- isDisabled: !canInsertMention || !mentionProvider || !isTypeAheadAllowed
43
+ isDisabled: !canInsertMention || !mentionProvider || !isTypeAheadAllowed,
44
+ testId: TOOLBAR_BUTTON_TEST_ID.MENTION
43
45
  }));
44
46
  };
@@ -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 var TableButton = function TableButton(_ref) {
9
9
  var api = _ref.api;
@@ -45,6 +45,6 @@ export var TableButton = function TableButton(_ref) {
45
45
  }),
46
46
  onClick: onClick,
47
47
  ariaKeyshortcuts: getAriaKeyshortcuts(toggleTable),
48
- testId: "Table"
48
+ testId: TOOLBAR_BUTTON_TEST_ID.TABLE
49
49
  }));
50
50
  };
@@ -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';
@@ -46,6 +47,7 @@ export var TableSizePicker = function TableSizePicker(_ref) {
46
47
  }),
47
48
  onClick: onClick,
48
49
  isSelected: tableSelectorPopup.isOpen,
49
- ref: tableSizePickerRef
50
+ ref: tableSizePickerRef,
51
+ testId: TOOLBAR_BUTTON_TEST_ID.TABLE_SELECTOR
50
52
  })));
51
53
  };
@@ -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 var TaskListButton = function TaskListButton(_ref) {
9
9
  var api = _ref.api;
@@ -33,6 +33,7 @@ export var TaskListButton = function TaskListButton(_ref) {
33
33
  size: "small"
34
34
  }),
35
35
  onClick: onClick,
36
- ariaKeyshortcuts: "[ ] Space"
36
+ ariaKeyshortcuts: "[ ] Space",
37
+ testId: TOOLBAR_BUTTON_TEST_ID.TASK_LIST
37
38
  }));
38
39
  };
@@ -0,0 +1,10 @@
1
+ import { type DispatchAnalyticsEvent } from '@atlaskit/editor-common/analytics';
2
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
+ type ToolbarActionExperienceOptions = {
4
+ dispatchAnalyticsEvent: DispatchAnalyticsEvent;
5
+ refs: {
6
+ popupsMountPoint?: HTMLElement;
7
+ };
8
+ };
9
+ export declare const getToolbarActionExperiencesPlugin: ({ refs, dispatchAnalyticsEvent, }: ToolbarActionExperienceOptions) => SafePlugin<any>;
10
+ export {};
@@ -0,0 +1,57 @@
1
+ import type { ExperienceCheck, ExperienceCheckCallback, ExperienceCheckResult } from '@atlaskit/editor-common/experiences';
2
+ import type { EditorView } from '@atlaskit/editor-prosemirror/view';
3
+ /**
4
+ * DOM marker selectors for node types inserted via toolbar actions.
5
+ * Matches outermost wrapper elements set synchronously by ReactNodeView
6
+ * (`{nodeTypeName}View-content-wrap`) or schema `toDOM` attributes.
7
+ */
8
+ export declare const NODE_INSERT_MARKERS: {
9
+ readonly TABLE: ".tableView-content-wrap";
10
+ readonly LAYOUT: ".layoutSectionView-content-wrap";
11
+ readonly LAYOUT_COLUMN: ".layoutColumnView-content-wrap";
12
+ readonly TASK_LIST: "[data-node-type=\"actionList\"]";
13
+ readonly TASK_ITEM: ".taskItemView-content-wrap";
14
+ };
15
+ export declare const isToolbarButtonClick: (target: HTMLElement, testId: string) => boolean;
16
+ /**
17
+ * ExperienceCheck that observes popup mount point and all its
18
+ * `[data-editor-popup]` children with `{ childList: true }` (no subtree).
19
+ *
20
+ * Detects when a popup containing the given nested element is added to the
21
+ * DOM — either as a new `[data-editor-popup]` direct child, or as content
22
+ * rendered inside an existing `[data-editor-popup]` wrapper.
23
+ */
24
+ export declare const TYPEAHEAD_DECORATION_SELECTOR = "[data-type-ahead=\"typeaheadDecoration\"]";
25
+ export declare const handleTypeAheadOpenDomMutation: ({ mutations, }: {
26
+ mutations: MutationRecord[];
27
+ }) => ExperienceCheckResult | undefined;
28
+ export declare class ExperienceCheckPopupMutation implements ExperienceCheck {
29
+ private nestedElementQuery;
30
+ private getTarget;
31
+ private getEditorDom;
32
+ private observers;
33
+ constructor(nestedElementQuery: string, getTarget: () => HTMLElement | undefined | null, getEditorDom: () => HTMLElement | undefined | null);
34
+ start(callback: ExperienceCheckCallback): void;
35
+ stop(): void;
36
+ }
37
+ /**
38
+ * Returns the narrow parent DOM element at the current selection, suitable
39
+ * for observing with `{ childList: true }` (no subtree).
40
+ *
41
+ * Uses the resolved position's depth to find the block node at the cursor
42
+ * via `nodeDOM`, then returns its `parentElement` — the container whose
43
+ * direct children change when content is inserted at this position.
44
+ *
45
+ * Falls back to `domAtPos` if `nodeDOM` is unavailable.
46
+ */
47
+ export declare const getParentDOMAtSelection: (editorView?: EditorView) => HTMLElement | null;
48
+ /**
49
+ * Evaluates DOM mutations to detect a node insert action.
50
+ *
51
+ * Uses two strategies:
52
+ * 1. Marker-based: checks `addedNodes` against known node insert selectors.
53
+ * 2. Structure-based: detects element add+remove (block-level replacement).
54
+ */
55
+ export declare const handleEditorNodeInsertDomMutation: ({ mutations, }: {
56
+ mutations: MutationRecord[];
57
+ }) => ExperienceCheckResult | undefined;