@atlaskit/editor-plugin-synced-block 5.1.9 → 5.1.10

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 (55) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/editor-commands/index.js +2 -2
  3. package/dist/cjs/pm-plugins/experience-tracking/create-reference-experience.js +26 -26
  4. package/dist/cjs/pm-plugins/experience-tracking/create-source-experience.js +14 -30
  5. package/dist/cjs/pm-plugins/experience-tracking/delete-reference-experience.js +175 -0
  6. package/dist/cjs/pm-plugins/experience-tracking/delete-source-experience.js +103 -0
  7. package/dist/cjs/pm-plugins/experience-tracking/get-experience-tracking-plugins.js +30 -0
  8. package/dist/cjs/pm-plugins/experience-tracking/provider-only-experiences.js +128 -0
  9. package/dist/cjs/pm-plugins/utils/experience-tracking-utils.js +85 -0
  10. package/dist/cjs/types/index.js +5 -2
  11. package/dist/cjs/ui/CreateSyncedBlockDropdownItem.js +40 -8
  12. package/dist/cjs/ui/DeleteConfirmationModal.js +3 -1
  13. package/dist/cjs/ui/floating-toolbar.js +4 -2
  14. package/dist/es2019/editor-commands/index.js +2 -2
  15. package/dist/es2019/pm-plugins/experience-tracking/create-reference-experience.js +27 -23
  16. package/dist/es2019/pm-plugins/experience-tracking/create-source-experience.js +14 -27
  17. package/dist/es2019/pm-plugins/experience-tracking/delete-reference-experience.js +181 -0
  18. package/dist/es2019/pm-plugins/experience-tracking/delete-source-experience.js +98 -0
  19. package/dist/es2019/pm-plugins/experience-tracking/get-experience-tracking-plugins.js +24 -0
  20. package/dist/es2019/pm-plugins/experience-tracking/provider-only-experiences.js +127 -0
  21. package/dist/es2019/pm-plugins/utils/experience-tracking-utils.js +65 -0
  22. package/dist/es2019/types/index.js +4 -1
  23. package/dist/es2019/ui/CreateSyncedBlockDropdownItem.js +38 -3
  24. package/dist/es2019/ui/DeleteConfirmationModal.js +3 -1
  25. package/dist/es2019/ui/floating-toolbar.js +3 -1
  26. package/dist/esm/editor-commands/index.js +2 -2
  27. package/dist/esm/pm-plugins/experience-tracking/create-reference-experience.js +26 -25
  28. package/dist/esm/pm-plugins/experience-tracking/create-source-experience.js +14 -29
  29. package/dist/esm/pm-plugins/experience-tracking/delete-reference-experience.js +169 -0
  30. package/dist/esm/pm-plugins/experience-tracking/delete-source-experience.js +97 -0
  31. package/dist/esm/pm-plugins/experience-tracking/get-experience-tracking-plugins.js +30 -0
  32. package/dist/esm/pm-plugins/experience-tracking/provider-only-experiences.js +122 -0
  33. package/dist/esm/pm-plugins/utils/experience-tracking-utils.js +79 -0
  34. package/dist/esm/types/index.js +4 -1
  35. package/dist/esm/ui/CreateSyncedBlockDropdownItem.js +40 -8
  36. package/dist/esm/ui/DeleteConfirmationModal.js +3 -1
  37. package/dist/esm/ui/floating-toolbar.js +4 -2
  38. package/dist/types/pm-plugins/experience-tracking/create-reference-experience.d.ts +2 -9
  39. package/dist/types/pm-plugins/experience-tracking/create-source-experience.d.ts +4 -15
  40. package/dist/types/pm-plugins/experience-tracking/delete-reference-experience.d.ts +13 -0
  41. package/dist/types/pm-plugins/experience-tracking/delete-source-experience.d.ts +12 -0
  42. package/dist/types/pm-plugins/experience-tracking/get-experience-tracking-plugins.d.ts +2 -13
  43. package/dist/types/pm-plugins/experience-tracking/provider-only-experiences.d.ts +3 -0
  44. package/dist/types/pm-plugins/utils/experience-tracking-utils.d.ts +9 -0
  45. package/dist/types/types/index.d.ts +15 -0
  46. package/dist/types-ts4.5/pm-plugins/experience-tracking/create-reference-experience.d.ts +2 -9
  47. package/dist/types-ts4.5/pm-plugins/experience-tracking/create-source-experience.d.ts +4 -15
  48. package/dist/types-ts4.5/pm-plugins/experience-tracking/delete-reference-experience.d.ts +13 -0
  49. package/dist/types-ts4.5/pm-plugins/experience-tracking/delete-source-experience.d.ts +12 -0
  50. package/dist/types-ts4.5/pm-plugins/experience-tracking/get-experience-tracking-plugins.d.ts +2 -13
  51. package/dist/types-ts4.5/pm-plugins/experience-tracking/provider-only-experiences.d.ts +3 -0
  52. package/dist/types-ts4.5/pm-plugins/utils/experience-tracking-utils.d.ts +9 -0
  53. package/dist/types-ts4.5/types/index.d.ts +15 -0
  54. package/package.json +5 -5
  55. package/build/tsconfig.json +0 -22
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.wasSyncBlockDeletedOrAddedByHistory = exports.getTarget = exports.getRemovedResourceIds = exports.getAddedResourceIds = void 0;
7
+ var _utils = require("@atlaskit/editor-common/utils");
8
+ var _utils2 = require("@atlaskit/editor-prosemirror/utils");
9
+ var targetEl;
10
+ var getTarget = exports.getTarget = function getTarget(containerElement) {
11
+ if (!targetEl) {
12
+ var element = containerElement === null || containerElement === void 0 ? void 0 : containerElement.querySelector('.ProseMirror');
13
+ if (!element || !(element instanceof HTMLElement)) {
14
+ return null;
15
+ }
16
+ targetEl = element;
17
+ }
18
+ return targetEl;
19
+ };
20
+ var wasSyncBlockDeletedOrAddedByHistory = exports.wasSyncBlockDeletedOrAddedByHistory = function wasSyncBlockDeletedOrAddedByHistory(tr, oldState, newState) {
21
+ var historyMeta = tr.getMeta(_utils.pmHistoryPluginKey);
22
+ if (!Boolean(historyMeta)) {
23
+ return {};
24
+ }
25
+ var syncBlock = newState.schema.nodes.syncBlock;
26
+ var oldSyncBlockNodes = (0, _utils2.findChildren)(oldState.doc, function (node) {
27
+ return node.type === syncBlock;
28
+ });
29
+ var newSyncBlockNodes = (0, _utils2.findChildren)(newState.doc, function (node) {
30
+ return node.type === syncBlock;
31
+ });
32
+ var oldSyncBlockIds = new Set(oldSyncBlockNodes.map(function (nodeWithPos) {
33
+ return nodeWithPos.node.attrs.localId;
34
+ }).filter(function (localId) {
35
+ return Boolean(localId);
36
+ }));
37
+ var newSyncBlockIds = new Set(newSyncBlockNodes.map(function (nodeWithPos) {
38
+ return nodeWithPos.node.attrs.localId;
39
+ }).filter(function (localId) {
40
+ return Boolean(localId);
41
+ }));
42
+ var hasDeletedSyncBlock = Array.from(oldSyncBlockIds).some(function (localId) {
43
+ return !newSyncBlockIds.has(localId);
44
+ });
45
+ var hasAddedSyncBlock = Array.from(newSyncBlockIds).some(function (localId) {
46
+ return !oldSyncBlockIds.has(localId);
47
+ });
48
+ return {
49
+ hasDeletedSyncBlock: hasDeletedSyncBlock,
50
+ hasAddedSyncBlock: hasAddedSyncBlock,
51
+ isUndo: historyMeta.redo === false
52
+ };
53
+ };
54
+ var getResourceIds = function getResourceIds(nodes, resourceIds, query) {
55
+ nodes.forEach(function (node) {
56
+ if (!(node instanceof HTMLElement)) {
57
+ return;
58
+ }
59
+ var syncBlockElements = node.querySelectorAll(query);
60
+ syncBlockElements.forEach(function (element) {
61
+ var resourceId = element.getAttribute('resourceid');
62
+ if (resourceId) {
63
+ resourceIds.push(resourceId);
64
+ }
65
+ });
66
+ });
67
+ };
68
+ var getAddedResourceIds = exports.getAddedResourceIds = function getAddedResourceIds(mutations, query) {
69
+ var resourceIds = [];
70
+ mutations.forEach(function (mutation) {
71
+ if (mutation.type === 'childList') {
72
+ getResourceIds(mutation.addedNodes, resourceIds, query);
73
+ }
74
+ });
75
+ return resourceIds;
76
+ };
77
+ var getRemovedResourceIds = exports.getRemovedResourceIds = function getRemovedResourceIds(mutations, query) {
78
+ var resourceIds = [];
79
+ mutations.forEach(function (mutation) {
80
+ if (mutation.type === 'childList') {
81
+ getResourceIds(mutation.removedNodes, resourceIds, query);
82
+ }
83
+ });
84
+ return resourceIds;
85
+ };
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.FLAG_ID = void 0;
6
+ exports.FLAG_ID = exports.EXPERIENCE_ABORT_REASON = void 0;
7
7
  var FLAG_ID = exports.FLAG_ID = /*#__PURE__*/function (FLAG_ID) {
8
8
  FLAG_ID["CANNOT_DELETE_WHEN_OFFLINE"] = "cannot-delete-when-offline";
9
9
  FLAG_ID["CANNOT_EDIT_WHEN_OFFLINE"] = "cannot-edit-when-offline";
@@ -11,4 +11,7 @@ var FLAG_ID = exports.FLAG_ID = /*#__PURE__*/function (FLAG_ID) {
11
11
  FLAG_ID["FAIL_TO_DELETE"] = "fail-to-delete";
12
12
  FLAG_ID["SYNC_BLOCK_COPIED"] = "sync-block-copied";
13
13
  return FLAG_ID;
14
- }({});
14
+ }({});
15
+ var EXPERIENCE_ABORT_REASON = exports.EXPERIENCE_ABORT_REASON = {
16
+ EDITOR_DESTROYED: 'editor-destroyed'
17
+ };
@@ -51,24 +51,56 @@ var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref)
51
51
  }),
52
52
  onClick: onClick,
53
53
  isDisabled: isOffline,
54
- testId: 'create-synced-block-block-menu-btn',
54
+ testId: "create-synced-block-block-menu-btn",
55
55
  elemAfter: /*#__PURE__*/_react.default.createElement(_lozenge.default, {
56
56
  appearance: "new"
57
57
  }, formatMessage(_messages.blockMenuMessages.newLozenge))
58
58
  }, formatMessage(_messages.blockMenuMessages.createSyncedBlock));
59
59
  };
60
- var CreateOrCopySyncedBlockDropdownItem = exports.CreateOrCopySyncedBlockDropdownItem = function CreateOrCopySyncedBlockDropdownItem(_ref2) {
61
- var _menuTriggerByNode$no;
62
- var api = _ref2.api,
63
- enableSourceSyncedBlockCreation = _ref2.enableSourceSyncedBlockCreation;
64
- var _useSharedPluginState2 = (0, _hooks.useSharedPluginStateWithSelector)(api, ['blockControls'], function (states) {
60
+ var CopySyncedBlockDropdownItem = function CopySyncedBlockDropdownItem(_ref2) {
61
+ var api = _ref2.api;
62
+ var _useIntl2 = (0, _reactIntlNext.useIntl)(),
63
+ formatMessage = _useIntl2.formatMessage;
64
+ var _useSharedPluginState2 = (0, _hooks.useSharedPluginStateWithSelector)(api, ['connectivity'], function (states) {
65
+ var _states$connectivityS2;
66
+ return {
67
+ mode: (_states$connectivityS2 = states.connectivityState) === null || _states$connectivityS2 === void 0 ? void 0 : _states$connectivityS2.mode
68
+ };
69
+ }),
70
+ mode = _useSharedPluginState2.mode;
71
+ var onClick = function onClick() {
72
+ var _api$core3, _api$core4, _api$blockControls2;
73
+ api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(api === null || api === void 0 ? void 0 : api.syncedBlock.commands.copySyncedBlockReferenceToClipboard());
74
+ api === null || api === void 0 || (_api$core4 = api.core) === null || _api$core4 === void 0 || _api$core4.actions.execute(api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || (_api$blockControls2 = _api$blockControls2.commands) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.toggleBlockMenu({
75
+ closeMenu: true
76
+ }));
77
+ };
78
+ return /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarDropdownItem, {
79
+ elemBefore: /*#__PURE__*/_react.default.createElement(_editorToolbar.SyncBlocksIcon, {
80
+ label: ""
81
+ }),
82
+ onClick: onClick,
83
+ isDisabled: (0, _editorPluginConnectivity.isOfflineMode)(mode),
84
+ elemAfter: /*#__PURE__*/_react.default.createElement(_lozenge.default, {
85
+ appearance: "new"
86
+ }, formatMessage(_messages.blockMenuMessages.newLozenge))
87
+ }, formatMessage(_messages.blockMenuMessages.copySyncedBlock));
88
+ };
89
+ var CreateOrCopySyncedBlockDropdownItem = exports.CreateOrCopySyncedBlockDropdownItem = function CreateOrCopySyncedBlockDropdownItem(_ref3) {
90
+ var api = _ref3.api,
91
+ enableSourceSyncedBlockCreation = _ref3.enableSourceSyncedBlockCreation;
92
+ var _useSharedPluginState3 = (0, _hooks.useSharedPluginStateWithSelector)(api, ['blockControls'], function (states) {
65
93
  var _states$blockControls3, _states$blockControls4;
66
94
  return {
67
95
  menuTriggerByNode: (_states$blockControls3 = (_states$blockControls4 = states.blockControlsState) === null || _states$blockControls4 === void 0 ? void 0 : _states$blockControls4.menuTriggerByNode) !== null && _states$blockControls3 !== void 0 ? _states$blockControls3 : undefined
68
96
  };
69
97
  }),
70
- menuTriggerByNode = _useSharedPluginState2.menuTriggerByNode;
71
- if (!['syncBlock', 'bodiedSyncBlock'].includes((_menuTriggerByNode$no = menuTriggerByNode === null || menuTriggerByNode === void 0 ? void 0 : menuTriggerByNode.nodeType) !== null && _menuTriggerByNode$no !== void 0 ? _menuTriggerByNode$no : '') && enableSourceSyncedBlockCreation) {
98
+ menuTriggerByNode = _useSharedPluginState3.menuTriggerByNode;
99
+ if ((menuTriggerByNode === null || menuTriggerByNode === void 0 ? void 0 : menuTriggerByNode.nodeType) === 'syncBlock' || (menuTriggerByNode === null || menuTriggerByNode === void 0 ? void 0 : menuTriggerByNode.nodeType) === 'bodiedSyncBlock') {
100
+ return /*#__PURE__*/_react.default.createElement(CopySyncedBlockDropdownItem, {
101
+ api: api
102
+ });
103
+ } else if (enableSourceSyncedBlockCreation) {
72
104
  return /*#__PURE__*/_react.default.createElement(CreateSyncedBlockDropdownItem, {
73
105
  api: api
74
106
  });
@@ -14,6 +14,7 @@ var _hooks = require("@atlaskit/editor-common/hooks");
14
14
  var _messages = require("@atlaskit/editor-common/messages");
15
15
  var _editorPluginConnectivity = require("@atlaskit/editor-plugin-connectivity");
16
16
  var _modalDialog = _interopRequireWildcard(require("@atlaskit/modal-dialog"));
17
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
17
18
  var _compiled = require("@atlaskit/primitives/compiled");
18
19
  var _main = require("../pm-plugins/main");
19
20
  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); }
@@ -117,6 +118,7 @@ var DeleteConfirmationModal = exports.DeleteConfirmationModal = function DeleteC
117
118
  onClick: handleClick(true),
118
119
  autoFocus: true,
119
120
  isDisabled: (0, _editorPluginConnectivity.isOfflineMode)(mode),
120
- isLoading: bodiedSyncBlockDeletionStatus === 'processing'
121
+ isLoading: bodiedSyncBlockDeletionStatus === 'processing',
122
+ testId: (0, _platformFeatureFlags.fg)('platform_synced_block_dogfooding') ? 'synced-block-delete-confirmation-modal-delete-button' : undefined
121
123
  }, formatMessage(_messages.syncBlockMessages.deleteConfirmationModalDeleteButton)))));
122
124
  };
@@ -15,6 +15,7 @@ var _editorSyncedBlockProvider = require("@atlaskit/editor-synced-block-provider
15
15
  var _copy = _interopRequireDefault(require("@atlaskit/icon/core/copy"));
16
16
  var _delete = _interopRequireDefault(require("@atlaskit/icon/core/delete"));
17
17
  var _edit = _interopRequireDefault(require("@atlaskit/icon/core/edit"));
18
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
18
19
  var _editorCommands = require("../editor-commands");
19
20
  var _utils2 = require("../pm-plugins/utils/utils");
20
21
  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); }
@@ -47,7 +48,8 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(stat
47
48
  type: 'button',
48
49
  title: formatMessage(_messages.default.delete),
49
50
  onClick: (0, _editorCommands.removeSyncedBlock)(api),
50
- icon: _delete.default
51
+ icon: _delete.default,
52
+ testId: (0, _platformFeatureFlags.fg)('platform_synced_block_dogfooding') ? 'reference-synced-block-delete-button' : undefined
51
53
  }, hoverDecorationProps(nodeType, _consts.akEditorSelectedNodeClassName));
52
54
  items.push(deleteButton);
53
55
  } else {
@@ -58,7 +60,7 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(stat
58
60
  icon: _copy.default,
59
61
  title: formatMessage(_messages.syncBlockMessages.copySyncBlockLabel),
60
62
  showTitle: false,
61
- tooltipContent: formatMessage(_messages.syncBlockMessages.copySyncBlockTooltip),
63
+ tooltipContent: formatMessage(_messages.syncBlockMessages.copySyncedBlockTooltip),
62
64
  onClick: (0, _editorCommands.copySyncedBlockReferenceToClipboard)(syncBlockStore, api)
63
65
  }, hoverDecorationProps(nodeType, _consts.akEditorSelectedNodeClassName));
64
66
  items.push(copyButton);
@@ -59,8 +59,8 @@ export const createSyncedBlock = ({
59
59
  syncBlockStore.sourceManager.createBodiedSyncBlockNode(attrs);
60
60
  tr.replaceWith(conversionInfo.from > 0 ? conversionInfo.from - 1 : 0, conversionInfo.to, newBodiedSyncBlockNode).scrollIntoView();
61
61
 
62
- // set selection to the end of the previous selection + 1 for the position taken up by the start of the new synced block
63
- tr.setSelection(TextSelection.create(tr.doc, conversionInfo.to + 1));
62
+ // set selection to the start of the previous selection for the position taken up by the start of the new synced block
63
+ tr.setSelection(TextSelection.create(tr.doc, conversionInfo.from));
64
64
  }
65
65
 
66
66
  // This transaction will be intercepted in filterTransaction and dispatched when saving to backend succeeds
@@ -2,13 +2,14 @@ import { ACTION_SUBJECT, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analy
2
2
  import { Experience, ExperienceCheckDomMutation, ExperienceCheckTimeout } from '@atlaskit/editor-common/experiences';
3
3
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
4
4
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
5
+ import { EXPERIENCE_ABORT_REASON } from '../../types';
6
+ import { getAddedResourceIds, wasSyncBlockDeletedOrAddedByHistory, getTarget } from '../utils/experience-tracking-utils';
5
7
  const isPastedFromFabricEditor = html => !!html && html.indexOf('data-pm-slice="') >= 0;
6
8
  const pluginKey = new PluginKey('createReferenceSyncBlockExperience');
7
9
  const START_METHOD = {
8
- PASTE: 'paste'
9
- };
10
- const ABORT_REASON = {
11
- EDITOR_DESTROYED: 'editor-destroyed'
10
+ PASTE: 'paste',
11
+ UNDO: 'undo',
12
+ REDO: 'redo'
12
13
  };
13
14
 
14
15
  /**
@@ -32,7 +33,7 @@ export const getCreateReferenceExperiencePlugin = ({
32
33
  return {
33
34
  destroy: () => {
34
35
  experience.abort({
35
- reason: ABORT_REASON.EDITOR_DESTROYED
36
+ reason: EXPERIENCE_ABORT_REASON.EDITOR_DESTROYED
36
37
  });
37
38
  }
38
39
  };
@@ -54,6 +55,20 @@ export const getCreateReferenceExperiencePlugin = ({
54
55
  });
55
56
  }
56
57
  }
58
+ },
59
+ appendTransaction: (transactions, oldState, newState) => {
60
+ transactions.forEach(tr => {
61
+ const {
62
+ hasAddedSyncBlock,
63
+ isUndo
64
+ } = wasSyncBlockDeletedOrAddedByHistory(tr, oldState, newState);
65
+ if (hasAddedSyncBlock) {
66
+ experience.start({
67
+ method: isUndo ? START_METHOD.UNDO : START_METHOD.REDO
68
+ });
69
+ }
70
+ });
71
+ return null;
57
72
  }
58
73
  });
59
74
  };
@@ -70,21 +85,20 @@ const getCreateReferenceExperience = ({
70
85
  onDomMutation: ({
71
86
  mutations
72
87
  }) => {
73
- if (mutations.some(isReferenceSyncBlockAddedInMutation)) {
88
+ const insertedResourceIds = getAddedResourceIds(mutations, '[data-prosemirror-node-name="syncBlock"]');
89
+ if (insertedResourceIds.length > 0) {
74
90
  return {
75
- status: 'success'
91
+ status: 'success',
92
+ metadata: {
93
+ insertedResourceIds
94
+ }
76
95
  };
77
96
  }
78
97
  return undefined;
79
98
  },
80
99
  observeConfig: () => {
81
- var _refs$containerElemen;
82
- const proseMirrorElement = (_refs$containerElemen = refs.containerElement) === null || _refs$containerElemen === void 0 ? void 0 : _refs$containerElemen.querySelector('.ProseMirror');
83
- if (!proseMirrorElement || !(proseMirrorElement instanceof HTMLElement)) {
84
- return null;
85
- }
86
100
  return {
87
- target: proseMirrorElement,
101
+ target: getTarget(refs.containerElement),
88
102
  options: {
89
103
  childList: true
90
104
  }
@@ -92,14 +106,4 @@ const getCreateReferenceExperience = ({
92
106
  }
93
107
  })]
94
108
  });
95
- };
96
- const isReferenceSyncBlockAddedInMutation = ({
97
- type,
98
- addedNodes
99
- }) => type === 'childList' && [...addedNodes].some(isReferenceSyncBlockNode);
100
- const isReferenceSyncBlockNode = node => {
101
- if (!(node instanceof HTMLElement)) {
102
- return false;
103
- }
104
- return !!node.querySelector('[data-prosemirror-node-name="syncBlock"]');
105
109
  };
@@ -3,10 +3,9 @@ import { ACTION_SUBJECT, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analy
3
3
  import { Experience, ExperienceCheckDomMutation, ExperienceCheckTimeout, getPopupContainerFromEditorView, popupWithNestedElement } from '@atlaskit/editor-common/experiences';
4
4
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
5
5
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
6
+ import { EXPERIENCE_ABORT_REASON } from '../../types';
7
+ import { getAddedResourceIds, getTarget } from '../utils/experience-tracking-utils';
6
8
  const pluginKey = new PluginKey('createSourceSyncBlockExperience');
7
- const ABORT_REASON = {
8
- EDITOR_DESTROYED: 'editor-destroyed'
9
- };
10
9
  const START_METHOD = {
11
10
  BLOCK_MENU: 'block-menu',
12
11
  PINNED_TOOLBAR: 'pinned-toolbar',
@@ -19,8 +18,8 @@ const syncedBlockCreateButtonIds = new Set(SYNCED_BLOCK_CREATE_BUTTON_IDS);
19
18
  * This experience tracks when a source sync block is inserted.
20
19
  *
21
20
  * Start: When user inserts a sync block via block menu, quick insert or pinned toolbar
22
- * Success: When the sync block is added to the DOM within 2000ms of start
23
- * Failure: When 500ms passes without the source sync block being added to the DOM
21
+ * Success: When the sync block is added to the DOM within 3000ms of start
22
+ * Failure: When 3000ms passes without the source sync block being added to the DOM
24
23
  */
25
24
  export const getCreateSourceExperiencePlugin = ({
26
25
  refs,
@@ -37,8 +36,7 @@ export const getCreateSourceExperiencePlugin = ({
37
36
  };
38
37
  const experience = getCreateSourceExperience({
39
38
  refs,
40
- dispatchAnalyticsEvent,
41
- syncBlockStore
39
+ dispatchAnalyticsEvent
42
40
  });
43
41
  syncBlockStore.sourceManager.setCreateExperience(experience);
44
42
  const unbindClickListener = bind(document, {
@@ -90,7 +88,7 @@ export const getCreateSourceExperiencePlugin = ({
90
88
  return {
91
89
  destroy: () => {
92
90
  experience.abort({
93
- reason: ABORT_REASON.EDITOR_DESTROYED
91
+ reason: EXPERIENCE_ABORT_REASON.EDITOR_DESTROYED
94
92
  });
95
93
  unbindClickListener();
96
94
  unbindKeydownListener();
@@ -107,26 +105,25 @@ const getCreateSourceExperience = ({
107
105
  actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE,
108
106
  dispatchAnalyticsEvent,
109
107
  checks: [new ExperienceCheckTimeout({
110
- durationMs: 2000
108
+ durationMs: 3000
111
109
  }), new ExperienceCheckDomMutation({
112
110
  onDomMutation: ({
113
111
  mutations
114
112
  }) => {
115
- if (mutations.some(isSourceSyncBlockAddedInMutation)) {
113
+ const createdResourceIds = getAddedResourceIds(mutations, '[data-prosemirror-node-name="bodiedSyncBlock"]');
114
+ if (createdResourceIds.length > 0) {
116
115
  return {
117
- status: 'success'
116
+ status: 'success',
117
+ metadata: {
118
+ createdResourceIds
119
+ }
118
120
  };
119
121
  }
120
122
  return undefined;
121
123
  },
122
124
  observeConfig: () => {
123
- var _refs$containerElemen;
124
- const proseMirrorElement = (_refs$containerElemen = refs.containerElement) === null || _refs$containerElemen === void 0 ? void 0 : _refs$containerElemen.querySelector('.ProseMirror');
125
- if (!proseMirrorElement || !(proseMirrorElement instanceof HTMLElement)) {
126
- return null;
127
- }
128
125
  return {
129
- target: proseMirrorElement,
126
+ target: getTarget(refs.containerElement),
130
127
  options: {
131
128
  childList: true
132
129
  }
@@ -166,14 +163,4 @@ const handleButtonClick = (testId, experience) => {
166
163
  };
167
164
  const isEnterKey = key => {
168
165
  return key === 'Enter';
169
- };
170
- const isSourceSyncBlockAddedInMutation = ({
171
- type,
172
- addedNodes
173
- }) => type === 'childList' && [...addedNodes].some(isSourceSyncBlockNode);
174
- const isSourceSyncBlockNode = node => {
175
- if (!(node instanceof HTMLElement)) {
176
- return false;
177
- }
178
- return !!node.querySelector('[data-prosemirror-node-name="bodiedSyncBlock"]');
179
166
  };
@@ -0,0 +1,181 @@
1
+ import { bind } from 'bind-event-listener';
2
+ import { ACTION_SUBJECT, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
3
+ import { Experience, ExperienceCheckDomMutation, ExperienceCheckTimeout } from '@atlaskit/editor-common/experiences';
4
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
5
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
6
+ import { EXPERIENCE_ABORT_REASON } from '../../types';
7
+ import { getRemovedResourceIds, getTarget, wasSyncBlockDeletedOrAddedByHistory } from '../utils/experience-tracking-utils';
8
+ const pluginKey = new PluginKey('deleteReferenceSyncBlockExperience');
9
+ const START_METHOD = {
10
+ ELEMENT_TOOLBAR: 'element-toolbar',
11
+ DELETE: 'delete',
12
+ TYPED_OVER: 'typed-over',
13
+ CUT: 'cut',
14
+ UNDO: 'undo',
15
+ REDO: 'redo'
16
+ };
17
+
18
+ /**
19
+ * This experience tracks when a reference sync block is deleted.
20
+ *
21
+ * Start: When user deletes ref sync block from toolbar, presses delete when cursor is in front of ref sync block,
22
+ * presses any key with a ref sync block selected, cuts with a ref sync block selected, triggers undo/redo that deletes a ref sync block
23
+ * Success: When the sync block is removed from the DOM within 2000ms of start
24
+ * Failure: When 2000ms passes without the reference sync block being removed from the DOM
25
+ */
26
+ export const getDeleteReferenceExperiencePlugin = ({
27
+ refs,
28
+ dispatchAnalyticsEvent,
29
+ syncBlockStore
30
+ }) => {
31
+ const experience = getDeleteReferenceExperience({
32
+ refs,
33
+ dispatchAnalyticsEvent
34
+ });
35
+ syncBlockStore.sourceManager.setDeleteExperience(experience);
36
+ const unbindClickListener = bind(document, {
37
+ type: 'click',
38
+ listener: event => {
39
+ const target = event.target;
40
+ if (!target) {
41
+ return;
42
+ }
43
+ const button = target.closest('button[data-testid]');
44
+ if (!button || !(button instanceof HTMLButtonElement)) {
45
+ return;
46
+ }
47
+ const testId = button.dataset.testid;
48
+ if (isReferenceSyncedBlockDeleteButtonId(testId)) {
49
+ experience.start({
50
+ method: START_METHOD.ELEMENT_TOOLBAR
51
+ });
52
+ }
53
+ }
54
+ });
55
+ return new SafePlugin({
56
+ key: pluginKey,
57
+ props: {
58
+ handleDOMEvents: {
59
+ cut: view => {
60
+ const {
61
+ state
62
+ } = view;
63
+ if (hasSyncBlockInSelection(state.selection)) {
64
+ experience.start({
65
+ method: START_METHOD.CUT
66
+ });
67
+ }
68
+ return false;
69
+ },
70
+ keydown: (view, event) => {
71
+ const {
72
+ state
73
+ } = view;
74
+ const hasSelection = hasSyncBlockInSelection(state.selection);
75
+ const hasAdjacent = hasSyncBlockBeforeCursor(state.selection);
76
+ if (hasSelection) {
77
+ experience.start({
78
+ method: START_METHOD.TYPED_OVER
79
+ });
80
+ }
81
+ if (isDeleteKey(event.key) && hasAdjacent) {
82
+ experience.start({
83
+ method: START_METHOD.DELETE
84
+ });
85
+ }
86
+ return false;
87
+ }
88
+ }
89
+ },
90
+ appendTransaction: (transactions, oldState, newState) => {
91
+ transactions.forEach(tr => {
92
+ const {
93
+ hasDeletedSyncBlock,
94
+ isUndo
95
+ } = wasSyncBlockDeletedOrAddedByHistory(tr, oldState, newState);
96
+ if (hasDeletedSyncBlock) {
97
+ experience.start({
98
+ method: isUndo ? START_METHOD.UNDO : START_METHOD.REDO
99
+ });
100
+ }
101
+ });
102
+ return null;
103
+ },
104
+ view: () => {
105
+ return {
106
+ destroy: () => {
107
+ experience.abort({
108
+ reason: EXPERIENCE_ABORT_REASON.EDITOR_DESTROYED
109
+ });
110
+ unbindClickListener();
111
+ }
112
+ };
113
+ }
114
+ });
115
+ };
116
+ export const getDeleteReferenceExperience = ({
117
+ refs,
118
+ dispatchAnalyticsEvent
119
+ }) => {
120
+ return new Experience(ACTION_SUBJECT.SYNCED_BLOCK, {
121
+ actionSubjectId: ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_DELETE,
122
+ dispatchAnalyticsEvent,
123
+ checks: [new ExperienceCheckTimeout({
124
+ durationMs: 2000
125
+ }), new ExperienceCheckDomMutation({
126
+ onDomMutation: ({
127
+ mutations
128
+ }) => {
129
+ const deletedResourceIds = getRemovedResourceIds(mutations, '[data-prosemirror-node-name="syncBlock"]');
130
+ if (deletedResourceIds.length > 0) {
131
+ return {
132
+ status: 'success',
133
+ metadata: {
134
+ deletedResourceIds
135
+ }
136
+ };
137
+ }
138
+ return undefined;
139
+ },
140
+ observeConfig: () => {
141
+ return {
142
+ target: getTarget(refs.containerElement),
143
+ options: {
144
+ childList: true
145
+ }
146
+ };
147
+ }
148
+ })]
149
+ });
150
+ };
151
+ const isReferenceSyncedBlockDeleteButtonId = testId => testId === 'reference-synced-block-delete-button';
152
+ const isDeleteKey = key => {
153
+ return key === 'Delete' || key === 'Backspace';
154
+ };
155
+ const hasSyncBlockInSelection = selection => {
156
+ const {
157
+ syncBlock
158
+ } = selection.$from.doc.type.schema.nodes;
159
+ let found = false;
160
+ selection.$from.doc.nodesBetween(selection.from, selection.to, node => {
161
+ if (node.type === syncBlock) {
162
+ found = true;
163
+ return false;
164
+ }
165
+ // sync block nodes can only be found at the top level
166
+ return false;
167
+ });
168
+ return found;
169
+ };
170
+ const hasSyncBlockBeforeCursor = selection => {
171
+ if (!selection.empty) {
172
+ return false;
173
+ }
174
+ const {
175
+ syncBlock
176
+ } = selection.$from.doc.type.schema.nodes;
177
+ const {
178
+ nodeBefore
179
+ } = selection.$from;
180
+ return (nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.type) === syncBlock;
181
+ };