@atlaskit/editor-plugin-synced-block 5.2.1 → 5.3.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 (76) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/afm-jira/tsconfig.json +1 -1
  3. package/dist/cjs/editor-commands/index.js +43 -5
  4. package/dist/cjs/editor-commands/utils.js +20 -0
  5. package/dist/cjs/pm-plugins/menu-and-toolbar-experiences.js +267 -0
  6. package/dist/cjs/syncedBlockPlugin.js +21 -12
  7. package/dist/cjs/types/index.js +6 -3
  8. package/dist/cjs/ui/CreateSyncedBlockButton.js +2 -1
  9. package/dist/cjs/ui/CreateSyncedBlockDropdownItem.js +2 -1
  10. package/dist/cjs/ui/SyncBlockRefresher.js +18 -4
  11. package/dist/cjs/ui/SyncedLocationDropdown.js +14 -11
  12. package/dist/cjs/ui/floating-toolbar.js +21 -2
  13. package/dist/es2019/editor-commands/index.js +44 -4
  14. package/dist/es2019/editor-commands/utils.js +14 -0
  15. package/dist/es2019/pm-plugins/menu-and-toolbar-experiences.js +261 -0
  16. package/dist/es2019/syncedBlockPlugin.js +19 -12
  17. package/dist/es2019/types/index.js +5 -2
  18. package/dist/es2019/ui/CreateSyncedBlockButton.js +2 -1
  19. package/dist/es2019/ui/CreateSyncedBlockDropdownItem.js +2 -1
  20. package/dist/es2019/ui/SyncBlockRefresher.js +18 -4
  21. package/dist/es2019/ui/SyncedLocationDropdown.js +14 -11
  22. package/dist/es2019/ui/floating-toolbar.js +20 -3
  23. package/dist/esm/editor-commands/index.js +42 -4
  24. package/dist/esm/editor-commands/utils.js +14 -0
  25. package/dist/esm/pm-plugins/menu-and-toolbar-experiences.js +260 -0
  26. package/dist/esm/syncedBlockPlugin.js +21 -12
  27. package/dist/esm/types/index.js +5 -2
  28. package/dist/esm/ui/CreateSyncedBlockButton.js +2 -1
  29. package/dist/esm/ui/CreateSyncedBlockDropdownItem.js +2 -1
  30. package/dist/esm/ui/SyncBlockRefresher.js +18 -4
  31. package/dist/esm/ui/SyncedLocationDropdown.js +14 -11
  32. package/dist/esm/ui/floating-toolbar.js +22 -3
  33. package/dist/types/editor-commands/index.d.ts +8 -1
  34. package/dist/types/editor-commands/utils.d.ts +2 -0
  35. package/dist/types/pm-plugins/menu-and-toolbar-experiences.d.ts +12 -0
  36. package/dist/types/types/index.d.ts +5 -14
  37. package/dist/types-ts4.5/editor-commands/index.d.ts +8 -1
  38. package/dist/types-ts4.5/editor-commands/utils.d.ts +2 -0
  39. package/dist/types-ts4.5/pm-plugins/menu-and-toolbar-experiences.d.ts +12 -0
  40. package/dist/types-ts4.5/types/index.d.ts +5 -14
  41. package/package.json +3 -3
  42. package/dist/cjs/pm-plugins/experience-tracking/create-reference-experience.js +0 -113
  43. package/dist/cjs/pm-plugins/experience-tracking/create-source-experience.js +0 -169
  44. package/dist/cjs/pm-plugins/experience-tracking/delete-reference-experience.js +0 -175
  45. package/dist/cjs/pm-plugins/experience-tracking/delete-source-experience.js +0 -103
  46. package/dist/cjs/pm-plugins/experience-tracking/get-experience-tracking-plugins.js +0 -61
  47. package/dist/cjs/pm-plugins/experience-tracking/provider-only-experiences.js +0 -128
  48. package/dist/cjs/pm-plugins/utils/experience-tracking-utils.js +0 -85
  49. package/dist/es2019/pm-plugins/experience-tracking/create-reference-experience.js +0 -109
  50. package/dist/es2019/pm-plugins/experience-tracking/create-source-experience.js +0 -166
  51. package/dist/es2019/pm-plugins/experience-tracking/delete-reference-experience.js +0 -181
  52. package/dist/es2019/pm-plugins/experience-tracking/delete-source-experience.js +0 -98
  53. package/dist/es2019/pm-plugins/experience-tracking/get-experience-tracking-plugins.js +0 -46
  54. package/dist/es2019/pm-plugins/experience-tracking/provider-only-experiences.js +0 -127
  55. package/dist/es2019/pm-plugins/utils/experience-tracking-utils.js +0 -65
  56. package/dist/esm/pm-plugins/experience-tracking/create-reference-experience.js +0 -107
  57. package/dist/esm/pm-plugins/experience-tracking/create-source-experience.js +0 -163
  58. package/dist/esm/pm-plugins/experience-tracking/delete-reference-experience.js +0 -169
  59. package/dist/esm/pm-plugins/experience-tracking/delete-source-experience.js +0 -97
  60. package/dist/esm/pm-plugins/experience-tracking/get-experience-tracking-plugins.js +0 -55
  61. package/dist/esm/pm-plugins/experience-tracking/provider-only-experiences.js +0 -122
  62. package/dist/esm/pm-plugins/utils/experience-tracking-utils.js +0 -79
  63. package/dist/types/pm-plugins/experience-tracking/create-reference-experience.d.ts +0 -10
  64. package/dist/types/pm-plugins/experience-tracking/create-source-experience.d.ts +0 -10
  65. package/dist/types/pm-plugins/experience-tracking/delete-reference-experience.d.ts +0 -13
  66. package/dist/types/pm-plugins/experience-tracking/delete-source-experience.d.ts +0 -12
  67. package/dist/types/pm-plugins/experience-tracking/get-experience-tracking-plugins.d.ts +0 -5
  68. package/dist/types/pm-plugins/experience-tracking/provider-only-experiences.d.ts +0 -3
  69. package/dist/types/pm-plugins/utils/experience-tracking-utils.d.ts +0 -9
  70. package/dist/types-ts4.5/pm-plugins/experience-tracking/create-reference-experience.d.ts +0 -10
  71. package/dist/types-ts4.5/pm-plugins/experience-tracking/create-source-experience.d.ts +0 -10
  72. package/dist/types-ts4.5/pm-plugins/experience-tracking/delete-reference-experience.d.ts +0 -13
  73. package/dist/types-ts4.5/pm-plugins/experience-tracking/delete-source-experience.d.ts +0 -12
  74. package/dist/types-ts4.5/pm-plugins/experience-tracking/get-experience-tracking-plugins.d.ts +0 -5
  75. package/dist/types-ts4.5/pm-plugins/experience-tracking/provider-only-experiences.d.ts +0 -3
  76. package/dist/types-ts4.5/pm-plugins/utils/experience-tracking-utils.d.ts +0 -9
@@ -9,15 +9,18 @@ exports.getToolbarConfig = void 0;
9
9
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
10
  var _react = _interopRequireDefault(require("react"));
11
11
  var _messages = _interopRequireWildcard(require("@atlaskit/editor-common/messages"));
12
+ var _ui = require("@atlaskit/editor-common/ui");
12
13
  var _utils = require("@atlaskit/editor-prosemirror/utils");
13
14
  var _consts = require("@atlaskit/editor-shared-styles/consts");
14
15
  var _editorSyncedBlockProvider = require("@atlaskit/editor-synced-block-provider");
15
16
  var _copy = _interopRequireDefault(require("@atlaskit/icon/core/copy"));
16
17
  var _delete = _interopRequireDefault(require("@atlaskit/icon/core/delete"));
17
18
  var _edit = _interopRequireDefault(require("@atlaskit/icon/core/edit"));
19
+ var _linkBroken = _interopRequireDefault(require("@atlaskit/icon/core/link-broken"));
18
20
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
19
21
  var _editorCommands = require("../editor-commands");
20
22
  var _utils2 = require("../pm-plugins/utils/utils");
23
+ var _types = require("../types");
21
24
  var _SyncedLocationDropdown = require("./SyncedLocationDropdown");
22
25
  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); }
23
26
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
@@ -54,7 +57,7 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(stat
54
57
  title: formatMessage(_messages.default.delete),
55
58
  onClick: (0, _editorCommands.removeSyncedBlock)(api),
56
59
  icon: _delete.default,
57
- testId: (0, _platformFeatureFlags.fg)('platform_synced_block_dogfooding') ? 'reference-synced-block-delete-button' : undefined
60
+ testId: (0, _platformFeatureFlags.fg)('platform_synced_block_dogfooding') ? _types.SYNCED_BLOCK_BUTTON_TEST_ID.syncedBlockToolbarReferenceDelete : undefined
58
61
  }, hoverDecorationProps(nodeType, _consts.akEditorSelectedNodeClassName));
59
62
  items.push(deleteButton);
60
63
  } else {
@@ -72,7 +75,23 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(stat
72
75
  });
73
76
  }
74
77
  };
75
- items.push(syncedLocation);
78
+ var unsyncButton = {
79
+ type: 'custom',
80
+ fallback: [],
81
+ render: function render(view) {
82
+ return /*#__PURE__*/_react.default.createElement(_ui.FloatingToolbarButton, {
83
+ areAnyNewToolbarFlagsEnabled: true,
84
+ icon: /*#__PURE__*/_react.default.createElement(_linkBroken.default, {
85
+ label: ""
86
+ }),
87
+ title: formatMessage(_messages.syncBlockMessages.unsyncButton),
88
+ onClick: function onClick() {
89
+ return (0, _editorCommands.unsync)(syncBlockStore, isBodiedSyncBlock, view);
90
+ }
91
+ });
92
+ }
93
+ };
94
+ items.push(syncedLocation, unsyncButton);
76
95
  }
77
96
  var copyButton = _objectSpread({
78
97
  id: 'editor.syncedBlock.copy',
@@ -1,15 +1,19 @@
1
+ import { defaultSchema } from '@atlaskit/adf-schema/schema-default';
1
2
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
2
3
  import { copyDomNode, toDOM } from '@atlaskit/editor-common/copy-button';
4
+ import { DOMSerializer, Fragment } from '@atlaskit/editor-prosemirror/model';
3
5
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
4
6
  import { findSelectedNodeOfType, removeParentNodeOfType, removeSelectedNode, safeInsert } from '@atlaskit/editor-prosemirror/utils';
5
7
  import { fg } from '@atlaskit/platform-feature-flags';
6
8
  import { syncedBlockPluginKey } from '../pm-plugins/main';
7
9
  import { canBeConvertedToSyncBlock, findSyncBlock, findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
8
10
  import { FLAG_ID } from '../types';
11
+ import { pasteSyncBlockHTMLContent } from './utils';
9
12
  export const createSyncedBlock = ({
10
13
  tr,
11
14
  syncBlockStore,
12
- typeAheadInsert
15
+ typeAheadInsert,
16
+ fireAnalyticsEvent
13
17
  }) => {
14
18
  const {
15
19
  schema: {
@@ -41,9 +45,14 @@ export const createSyncedBlock = ({
41
45
  const conversionInfo = canBeConvertedToSyncBlock(tr.selection);
42
46
  if (!conversionInfo) {
43
47
  if (fg('platform_synced_block_dogfooding')) {
44
- var _syncBlockStore$sourc;
45
- (_syncBlockStore$sourc = syncBlockStore.sourceManager.createExperience) === null || _syncBlockStore$sourc === void 0 ? void 0 : _syncBlockStore$sourc.failure({
46
- reason: 'Selection is not allowed to be converted to sync block'
48
+ fireAnalyticsEvent === null || fireAnalyticsEvent === void 0 ? void 0 : fireAnalyticsEvent({
49
+ action: ACTION.ERROR,
50
+ actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
51
+ actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE,
52
+ attributes: {
53
+ error: 'Content cannot be converted to sync block'
54
+ },
55
+ eventType: EVENT_TYPE.OPERATIONAL
47
56
  });
48
57
  }
49
58
  return false;
@@ -170,4 +179,35 @@ export const removeSyncedBlock = api => (state, dispatch, _view) => {
170
179
  dispatch(removeTr);
171
180
  api === null || api === void 0 ? void 0 : api.core.actions.focus();
172
181
  return true;
182
+ };
183
+
184
+ /**
185
+ * Deletes (bodied)SyncBlock node and paste its content to the editor
186
+ */
187
+ export const unsync = (storeManager, isBodiedSyncBlock, view) => {
188
+ var _storeManager$referen, _storeManager$referen2;
189
+ if (!view) {
190
+ return false;
191
+ }
192
+ const {
193
+ state
194
+ } = view;
195
+ const syncBlock = findSyncBlockOrBodiedSyncBlock(state.schema, state.selection);
196
+ if (!syncBlock) {
197
+ return false;
198
+ }
199
+ if (isBodiedSyncBlock) {
200
+ return true;
201
+ }
202
+
203
+ // handle syncBlock unsync
204
+ const syncBlockContent = (_storeManager$referen = storeManager.referenceManager.getFromCache(syncBlock.node.attrs.resourceId)) === null || _storeManager$referen === void 0 ? void 0 : (_storeManager$referen2 = _storeManager$referen.data) === null || _storeManager$referen2 === void 0 ? void 0 : _storeManager$referen2.content;
205
+ if (!syncBlockContent) {
206
+ return false;
207
+ }
208
+
209
+ // use defaultSchema for serialization so we can serialize any type of nodes and marks despite current editor's schema might not allow it
210
+ const contentFragment = Fragment.fromJSON(defaultSchema, syncBlockContent);
211
+ const contentDOM = DOMSerializer.fromSchema(defaultSchema).serializeFragment(contentFragment);
212
+ return pasteSyncBlockHTMLContent(contentDOM, view);
173
213
  };
@@ -0,0 +1,14 @@
1
+ export const pasteSyncBlockHTMLContent = (contentDOM, view) => {
2
+ const tmpDiv = document.createElement('div');
3
+ tmpDiv.appendChild(contentDOM);
4
+
5
+ // This is required so that prosemirror can read the fragment context and slice properly
6
+ if (tmpDiv.firstChild instanceof HTMLElement) {
7
+ tmpDiv.firstChild.setAttribute('data-pm-slice', '0 0 []');
8
+
9
+ // As per requirement - when unsync reference block, it should render its content as copy&paste behaviour
10
+ // Hence here we call pasteHTML to evoke editor paste logic that handles any unsupported nodes/marks
11
+ return view.pasteHTML(tmpDiv.innerHTML);
12
+ }
13
+ return false;
14
+ };
@@ -0,0 +1,261 @@
1
+ import { bind } from 'bind-event-listener';
2
+ import { ACTION, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
3
+ import { Experience, EXPERIENCE_ID, ExperienceCheckDomMutation, ExperienceCheckTimeout, getNodeQuery, getPopupContainerFromEditorView, popupWithNestedElement } from '@atlaskit/editor-common/experiences';
4
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
5
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
6
+ import { SYNCED_BLOCK_BUTTON_TEST_ID } from '../types';
7
+ const TIMEOUT_DURATION = 30000;
8
+ const pluginKey = new PluginKey('syncedBlockMenuAndToolbarExperience');
9
+ const SYNCED_BLOCK_BUTTON_TEST_IDS = Object.values(SYNCED_BLOCK_BUTTON_TEST_ID);
10
+ const syncedBlockButtonIds = new Set(SYNCED_BLOCK_BUTTON_TEST_IDS);
11
+ let targetEl;
12
+ export const getMenuAndToolbarExperiencesPlugin = ({
13
+ refs,
14
+ dispatchAnalyticsEvent
15
+ }) => {
16
+ let popupsTargetEl;
17
+ let editorViewEl;
18
+ const getPopupsTarget = () => {
19
+ if (!popupsTargetEl) {
20
+ popupsTargetEl = refs.popupsMountPoint || refs.wrapperElement || getPopupContainerFromEditorView(editorViewEl);
21
+ }
22
+ return popupsTargetEl;
23
+ };
24
+ const createSourcePrimaryToolbarExperience = getCreateSourcePrimaryToolbarExperience({
25
+ refs,
26
+ dispatchAnalyticsEvent
27
+ });
28
+ const createSourceBlockMenuExperience = getCreateSourceBlockMenuExperience({
29
+ refs,
30
+ dispatchAnalyticsEvent
31
+ });
32
+ const createSourceQuickInsertMenuExperience = getCreateSourceQuickInsertMenuExperience({
33
+ refs,
34
+ dispatchAnalyticsEvent
35
+ });
36
+ const deleteReferenceSyncedBlockExperience = getDeleteReferenceSyncedBlockToolbarExperience({
37
+ refs,
38
+ dispatchAnalyticsEvent
39
+ });
40
+ const unbindClickListener = bind(document, {
41
+ type: 'click',
42
+ listener: event => {
43
+ const target = event.target;
44
+ if (!target) {
45
+ return;
46
+ }
47
+ const button = target.closest('button[data-testid]');
48
+ if (!button || !(button instanceof HTMLButtonElement)) {
49
+ return;
50
+ }
51
+ const testId = button.dataset.testid;
52
+ if (!isSyncedBlockButtonId(testId)) {
53
+ return;
54
+ }
55
+ handleButtonClick({
56
+ testId,
57
+ createSourcePrimaryToolbarExperience,
58
+ createSourceBlockMenuExperience,
59
+ createSourceQuickInsertMenuExperience,
60
+ deleteReferenceSyncedBlockExperience
61
+ });
62
+ }
63
+ });
64
+ const unbindKeydownListener = bind(document, {
65
+ type: 'keydown',
66
+ listener: event => {
67
+ if (isEnterKey(event.key)) {
68
+ const typeaheadPopup = popupWithNestedElement(getPopupsTarget(), '.fabric-editor-typeahead');
69
+ if (!typeaheadPopup || !(typeaheadPopup instanceof HTMLElement)) {
70
+ return;
71
+ }
72
+ const firstItem = typeaheadPopup.querySelector('[role="option"]');
73
+ if (!firstItem || !(firstItem instanceof HTMLElement)) {
74
+ return;
75
+ }
76
+ const testId = firstItem.dataset.testid;
77
+ if (testId === SYNCED_BLOCK_BUTTON_TEST_ID.quickInsertCreate) {
78
+ createSourceQuickInsertMenuExperience.start();
79
+ }
80
+ }
81
+ },
82
+ options: {
83
+ capture: true
84
+ }
85
+ });
86
+ return new SafePlugin({
87
+ key: pluginKey,
88
+ view: editorView => {
89
+ editorViewEl = editorView.dom;
90
+ return {
91
+ destroy: () => {
92
+ createSourcePrimaryToolbarExperience.abort({
93
+ reason: 'editor-destroyed'
94
+ });
95
+ createSourceBlockMenuExperience.abort({
96
+ reason: 'editor-destroyed'
97
+ });
98
+ createSourceQuickInsertMenuExperience.abort({
99
+ reason: 'editor-destroyed'
100
+ });
101
+ deleteReferenceSyncedBlockExperience.abort({
102
+ reason: 'editor-destroyed'
103
+ });
104
+ unbindClickListener();
105
+ unbindKeydownListener();
106
+ }
107
+ };
108
+ }
109
+ });
110
+ };
111
+ const getCreateSourcePrimaryToolbarExperience = ({
112
+ refs,
113
+ dispatchAnalyticsEvent
114
+ }) => {
115
+ return new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
116
+ action: ACTION.SYNCED_BLOCK_CREATE,
117
+ actionSubjectId: ACTION_SUBJECT_ID.PRIMARY_TOOLBAR,
118
+ dispatchAnalyticsEvent,
119
+ checks: [new ExperienceCheckTimeout({
120
+ durationMs: TIMEOUT_DURATION
121
+ }), syncedBlockAddedToDomCheck(refs)]
122
+ });
123
+ };
124
+ const getCreateSourceBlockMenuExperience = ({
125
+ refs,
126
+ dispatchAnalyticsEvent
127
+ }) => {
128
+ return new Experience(EXPERIENCE_ID.MENU_ACTION, {
129
+ action: ACTION.SYNCED_BLOCK_CREATE,
130
+ actionSubjectId: ACTION_SUBJECT_ID.BLOCK_MENU,
131
+ dispatchAnalyticsEvent,
132
+ checks: [new ExperienceCheckTimeout({
133
+ durationMs: TIMEOUT_DURATION
134
+ }), syncedBlockAddedToDomCheck(refs)]
135
+ });
136
+ };
137
+ const getCreateSourceQuickInsertMenuExperience = ({
138
+ refs,
139
+ dispatchAnalyticsEvent
140
+ }) => {
141
+ return new Experience(EXPERIENCE_ID.MENU_ACTION, {
142
+ action: ACTION.SYNCED_BLOCK_CREATE,
143
+ actionSubjectId: ACTION_SUBJECT_ID.QUICK_INSERT,
144
+ dispatchAnalyticsEvent,
145
+ checks: [new ExperienceCheckTimeout({
146
+ durationMs: TIMEOUT_DURATION
147
+ }), syncedBlockAddedToDomCheck(refs)]
148
+ });
149
+ };
150
+ const getDeleteReferenceSyncedBlockToolbarExperience = ({
151
+ refs,
152
+ dispatchAnalyticsEvent
153
+ }) => {
154
+ return new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
155
+ action: ACTION.REFERENCE_SYNCED_BLOCK_DELETE,
156
+ actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_TOOLBAR,
157
+ dispatchAnalyticsEvent,
158
+ checks: [new ExperienceCheckTimeout({
159
+ durationMs: TIMEOUT_DURATION
160
+ }), referenceSyncBlockRemovedFromDomCheck(refs)]
161
+ });
162
+ };
163
+ const isSyncedBlockButtonId = value => {
164
+ return !!value && syncedBlockButtonIds.has(value);
165
+ };
166
+ const handleButtonClick = ({
167
+ testId,
168
+ createSourcePrimaryToolbarExperience,
169
+ createSourceBlockMenuExperience,
170
+ createSourceQuickInsertMenuExperience,
171
+ deleteReferenceSyncedBlockExperience
172
+ }) => {
173
+ switch (testId) {
174
+ case SYNCED_BLOCK_BUTTON_TEST_ID.primaryToolbarCreate:
175
+ createSourcePrimaryToolbarExperience.start();
176
+ break;
177
+ case SYNCED_BLOCK_BUTTON_TEST_ID.blockMenuCreate:
178
+ createSourceBlockMenuExperience.start();
179
+ break;
180
+ case SYNCED_BLOCK_BUTTON_TEST_ID.quickInsertCreate:
181
+ createSourceQuickInsertMenuExperience.start();
182
+ break;
183
+ case SYNCED_BLOCK_BUTTON_TEST_ID.syncedBlockToolbarReferenceDelete:
184
+ deleteReferenceSyncedBlockExperience.start();
185
+ break;
186
+ default:
187
+ {
188
+ // Exhaustiveness check: if a new SyncedBlockToolbarButtonId is added
189
+ // but not handled above, TypeScript will error here.
190
+ const _exhaustiveCheck = testId;
191
+ return _exhaustiveCheck;
192
+ }
193
+ }
194
+ };
195
+ const isEnterKey = key => {
196
+ return key === 'Enter';
197
+ };
198
+ const getTarget = containerElement => {
199
+ if (!targetEl) {
200
+ const element = containerElement === null || containerElement === void 0 ? void 0 : containerElement.querySelector('.ProseMirror');
201
+ if (!element || !(element instanceof HTMLElement)) {
202
+ return null;
203
+ }
204
+ targetEl = element;
205
+ }
206
+ return targetEl;
207
+ };
208
+ const syncedBlockAddedToDomCheck = refs => new ExperienceCheckDomMutation({
209
+ onDomMutation: ({
210
+ mutations
211
+ }) => {
212
+ if (mutations.some(isBodiedSyncBlockAddedInMutation)) {
213
+ return {
214
+ status: 'success'
215
+ };
216
+ }
217
+ return undefined;
218
+ },
219
+ observeConfig: () => {
220
+ return {
221
+ target: getTarget(refs.containerElement),
222
+ options: {
223
+ childList: true
224
+ }
225
+ };
226
+ }
227
+ });
228
+ const isBodiedSyncBlockAddedInMutation = ({
229
+ type,
230
+ addedNodes
231
+ }) => {
232
+ return type === 'childList' && [...addedNodes].some(isBodiedSyncBlockWithinNode);
233
+ };
234
+ const isBodiedSyncBlockWithinNode = node => getNodeQuery('[data-prosemirror-node-name="bodiedSyncBlock"]')(node);
235
+ const referenceSyncBlockRemovedFromDomCheck = refs => new ExperienceCheckDomMutation({
236
+ onDomMutation: ({
237
+ mutations
238
+ }) => {
239
+ if (mutations.some(isSyncBlockRemovedInMutation)) {
240
+ return {
241
+ status: 'success'
242
+ };
243
+ }
244
+ return undefined;
245
+ },
246
+ observeConfig: () => {
247
+ return {
248
+ target: getTarget(refs.containerElement),
249
+ options: {
250
+ childList: true
251
+ }
252
+ };
253
+ }
254
+ });
255
+ const isSyncBlockRemovedInMutation = ({
256
+ type,
257
+ removedNodes
258
+ }) => {
259
+ return type === 'childList' && [...removedNodes].some(isSyncBlockWithinNode);
260
+ };
261
+ const isSyncBlockWithinNode = node => getNodeQuery('[data-prosemirror-node-name="syncBlock"]')(node);
@@ -7,8 +7,9 @@ import Lozenge from '@atlaskit/lozenge';
7
7
  import { fg } from '@atlaskit/platform-feature-flags';
8
8
  import { flushBodiedSyncBlocks, flushSyncBlocks } from './editor-actions';
9
9
  import { copySyncedBlockReferenceToClipboardEditorCommand, createSyncedBlock } from './editor-commands';
10
- import { getExperienceTrackingPlugins } from './pm-plugins/experience-tracking/get-experience-tracking-plugins';
11
10
  import { createPlugin, syncedBlockPluginKey } from './pm-plugins/main';
11
+ import { getMenuAndToolbarExperiencesPlugin } from './pm-plugins/menu-and-toolbar-experiences';
12
+ import { SYNCED_BLOCK_BUTTON_TEST_ID } from './types';
12
13
  import { getBlockMenuComponents } from './ui/block-menu-components';
13
14
  import { DeleteConfirmationModal } from './ui/DeleteConfirmationModal';
14
15
  import { Flag } from './ui/Flag';
@@ -40,26 +41,30 @@ export const syncedBlockPlugin = ({
40
41
  return [{
41
42
  name: 'syncedBlockPlugin',
42
43
  plugin: params => createPlugin(config, params, syncBlockStore, api)
43
- }, ...(fg('platform_synced_block_dogfooding') ? getExperienceTrackingPlugins({
44
- refs,
45
- dispatchAnalyticsEvent: payload => {
46
- var _api$analytics2;
47
- return api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions.fireAnalyticsEvent(payload);
48
- },
49
- syncBlockStore
50
- }) : [])];
44
+ }, ...(fg('platform_synced_block_dogfooding') ? [{
45
+ name: 'menuAndToolbarExperiencesPlugin',
46
+ plugin: () => getMenuAndToolbarExperiencesPlugin({
47
+ refs,
48
+ dispatchAnalyticsEvent: payload => {
49
+ var _api$analytics2, _api$analytics2$actio;
50
+ return api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : (_api$analytics2$actio = _api$analytics2.actions) === null || _api$analytics2$actio === void 0 ? void 0 : _api$analytics2$actio.fireAnalyticsEvent(payload);
51
+ }
52
+ })
53
+ }] : [])];
51
54
  },
52
55
  commands: {
53
56
  copySyncedBlockReferenceToClipboard: () => copySyncedBlockReferenceToClipboardEditorCommand(syncBlockStore, api),
54
57
  insertSyncedBlock: () => ({
55
58
  tr
56
59
  }) => {
60
+ var _api$analytics3;
57
61
  if (!(config !== null && config !== void 0 && config.enableSourceCreation)) {
58
62
  return null;
59
63
  }
60
64
  return createSyncedBlock({
61
65
  tr,
62
- syncBlockStore
66
+ syncBlockStore,
67
+ fireAnalyticsEvent: api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.fireAnalyticsEvent
63
68
  }) || null;
64
69
  }
65
70
  },
@@ -93,13 +98,15 @@ export const syncedBlockPlugin = ({
93
98
  label: formatMessage(blockTypeMessages.syncedBlock)
94
99
  }),
95
100
  action: (insert, state) => {
101
+ var _api$analytics4;
96
102
  return createSyncedBlock({
97
103
  tr: state.tr,
98
104
  syncBlockStore,
99
- typeAheadInsert: insert
105
+ typeAheadInsert: insert,
106
+ fireAnalyticsEvent: api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions.fireAnalyticsEvent
100
107
  });
101
108
  },
102
- testId: fg('platform_synced_block_dogfooding') ? 'create-synced-block-quick-insert-btn' : undefined
109
+ testId: fg('platform_synced_block_dogfooding') ? SYNCED_BLOCK_BUTTON_TEST_ID.quickInsertCreate : undefined
103
110
  }];
104
111
  },
105
112
  floatingToolbar: (state, intl) => getToolbarConfig(state, intl, api, syncBlockStore)
@@ -6,6 +6,9 @@ export let FLAG_ID = /*#__PURE__*/function (FLAG_ID) {
6
6
  FLAG_ID["SYNC_BLOCK_COPIED"] = "sync-block-copied";
7
7
  return FLAG_ID;
8
8
  }({});
9
- export const EXPERIENCE_ABORT_REASON = {
10
- EDITOR_DESTROYED: 'editor-destroyed'
9
+ export const SYNCED_BLOCK_BUTTON_TEST_ID = {
10
+ primaryToolbarCreate: 'create-synced-block-toolbar-btn',
11
+ blockMenuCreate: 'create-synced-block-block-menu-btn',
12
+ quickInsertCreate: 'create-synced-block-quick-insert-btn',
13
+ syncedBlockToolbarReferenceDelete: 'reference-synced-block-delete-btn'
11
14
  };
@@ -6,6 +6,7 @@ import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
6
6
  import { ToolbarButton, ToolbarTooltip } from '@atlaskit/editor-toolbar';
7
7
  import BlockSyncedIcon from '@atlaskit/icon-lab/core/block-synced';
8
8
  import { canBeConvertedToSyncBlock } from '../pm-plugins/utils/utils';
9
+ import { SYNCED_BLOCK_BUTTON_TEST_ID } from '../types';
9
10
  export const CreateSyncedBlockButton = ({
10
11
  api
11
12
  }) => {
@@ -45,7 +46,7 @@ export const CreateSyncedBlockButton = ({
45
46
  label: ""
46
47
  }),
47
48
  isDisabled: isDisabled,
48
- testId: "create-synced-block-toolbar-btn",
49
+ testId: SYNCED_BLOCK_BUTTON_TEST_ID.primaryToolbarCreate,
49
50
  onClick: onClick
50
51
  }));
51
52
  };
@@ -6,6 +6,7 @@ import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
6
6
  import { SyncBlocksIcon, ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
7
7
  import Lozenge from '@atlaskit/lozenge';
8
8
  import { canBeConvertedToSyncBlock } from '../pm-plugins/utils/utils';
9
+ import { SYNCED_BLOCK_BUTTON_TEST_ID } from '../types';
9
10
  const CreateSyncedBlockDropdownItem = ({
10
11
  api
11
12
  }) => {
@@ -43,7 +44,7 @@ const CreateSyncedBlockDropdownItem = ({
43
44
  }),
44
45
  onClick: onClick,
45
46
  isDisabled: isOffline,
46
- testId: "create-synced-block-block-menu-btn",
47
+ testId: SYNCED_BLOCK_BUTTON_TEST_ID.blockMenuCreate,
47
48
  elemAfter: /*#__PURE__*/React.createElement(Lozenge, {
48
49
  appearance: "new"
49
50
  }, formatMessage(blockMenuMessages.newLozenge))
@@ -1,9 +1,13 @@
1
1
  import { useEffect } from 'react';
2
2
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
3
+ import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
4
+ import { fg } from '@atlaskit/platform-feature-flags';
3
5
  export const SYNC_BLOCK_FETCH_INTERVAL = 3000;
4
6
 
5
- // Component that refreshes synced block subscriptions at regular intervals
6
- // this is a workaround for the subscription mechanism not being real-time
7
+ // Component that manages synced block data synchronization.
8
+ // When the feature flag 'platform_synced_block_dogfooding' is enabled,
9
+ // it uses provider-based GraphQL subscriptions for updates.
10
+ // When disabled, it falls back to polling at regular intervals.
7
11
  export const SyncBlockRefresher = ({
8
12
  syncBlockStoreManager,
9
13
  api
@@ -16,9 +20,19 @@ export const SyncBlockRefresher = ({
16
20
  mode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode
17
21
  };
18
22
  });
23
+ const featureFlagEnabled = fg('platform_synced_block_dogfooding');
24
+ const isOnline = !isOfflineMode(mode);
19
25
  useEffect(() => {
26
+ const useRealTimeSubscriptions = featureFlagEnabled && isOnline;
27
+ syncBlockStoreManager.referenceManager.setRealTimeSubscriptionsEnabled(useRealTimeSubscriptions);
28
+ }, [syncBlockStoreManager, featureFlagEnabled, isOnline]);
29
+ useEffect(() => {
30
+ const useRealTimeSubscriptions = featureFlagEnabled && isOnline;
31
+ if (useRealTimeSubscriptions) {
32
+ return;
33
+ }
20
34
  let interval = -1;
21
- if (mode !== 'offline') {
35
+ if (isOnline) {
22
36
  interval = window.setInterval(() => {
23
37
  var _document;
24
38
  // check if document is visible to avoid unnecessary refreshes
@@ -32,6 +46,6 @@ export const SyncBlockRefresher = ({
32
46
  return () => {
33
47
  window.clearInterval(interval);
34
48
  };
35
- }, [syncBlockStoreManager, mode]);
49
+ }, [syncBlockStoreManager, isOnline, featureFlagEnabled]);
36
50
  return null;
37
51
  };
@@ -39,17 +39,18 @@ const styles = {
39
39
  const ItemTitle = ({
40
40
  title,
41
41
  formatMessage,
42
- onSamePage,
42
+ onSameDocument,
43
43
  isSource,
44
- hasAccess
44
+ hasAccess,
45
+ productType
45
46
  }) => {
46
47
  return /*#__PURE__*/React.createElement(Inline, null, /*#__PURE__*/React.createElement(Box, {
47
48
  as: "span",
48
49
  xcss: styles.title
49
- }, title), onSamePage && /*#__PURE__*/React.createElement(Box, {
50
+ }, title), onSameDocument && /*#__PURE__*/React.createElement(Box, {
50
51
  as: "span",
51
52
  xcss: styles.note
52
- }, "\xA0- ", formatMessage(messages.syncedLocationDropdownTitleNote)), isSource && /*#__PURE__*/React.createElement(Box, {
53
+ }, "\xA0- ", formatMessage(productType === 'confluence-page' ? messages.syncedLocationDropdownTitleNoteForConfluencePage : messages.syncedLocationDropdownTitleNoteForJiraWorkItem)), isSource && /*#__PURE__*/React.createElement(Box, {
53
54
  as: "span",
54
55
  xcss: styles.lozenge
55
56
  }, /*#__PURE__*/React.createElement(Lozenge, null, formatMessage(messages.syncedLocationDropdownSourceLozenge))), !hasAccess && /*#__PURE__*/React.createElement(Box, {
@@ -66,7 +67,7 @@ const subTypeIconMap = {
66
67
  page: PageIcon,
67
68
  blogpost: QuotationMarkIcon
68
69
  };
69
- const getSubTypeIcon = subType => {
70
+ const getConfluenceSubTypeIcon = subType => {
70
71
  return subType && subType in subTypeIconMap ? subTypeIconMap[subType] : PageIcon;
71
72
  };
72
73
  const ProductIcon = ({
@@ -86,18 +87,19 @@ const ItemIcon = ({
86
87
  }) => {
87
88
  const {
88
89
  hasAccess,
89
- subType
90
+ subType,
91
+ productType
90
92
  } = reference;
91
- if (hasAccess) {
93
+ if (productType === 'confluence-page' && hasAccess) {
92
94
  return /*#__PURE__*/React.createElement(IconTile, {
93
- icon: getSubTypeIcon(subType),
95
+ icon: getConfluenceSubTypeIcon(subType),
94
96
  label: "",
95
97
  appearance: 'gray',
96
98
  size: "xsmall"
97
99
  });
98
100
  }
99
101
  return /*#__PURE__*/React.createElement(ProductIcon, {
100
- product: reference.productType
102
+ product: productType
101
103
  });
102
104
  };
103
105
  export const processReferenceData = (referenceData, intl) => {
@@ -230,9 +232,10 @@ const DropdownContent = ({
230
232
  }, /*#__PURE__*/React.createElement(ItemTitle, {
231
233
  title: reference.title || reference.url || '',
232
234
  formatMessage: formatMessage,
233
- onSamePage: reference.onSamePage,
235
+ onSameDocument: reference.onSameDocument,
234
236
  isSource: reference.isSource,
235
- hasAccess: reference.hasAccess
237
+ hasAccess: reference.hasAccess,
238
+ productType: reference.productType
236
239
  })))))));
237
240
  } else {
238
241
  return /*#__PURE__*/React.createElement(NoResultScreen, {