@atlaskit/editor-plugin-synced-block 5.2.0 → 5.2.2

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 (65) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/afm-jira/tsconfig.json +1 -1
  3. package/dist/cjs/editor-commands/index.js +10 -4
  4. package/dist/cjs/pm-plugins/menu-and-toolbar-experiences.js +267 -0
  5. package/dist/cjs/syncedBlockPlugin.js +21 -12
  6. package/dist/cjs/types/index.js +6 -3
  7. package/dist/cjs/ui/CreateSyncedBlockButton.js +2 -1
  8. package/dist/cjs/ui/CreateSyncedBlockDropdownItem.js +2 -1
  9. package/dist/cjs/ui/floating-toolbar.js +2 -1
  10. package/dist/es2019/editor-commands/index.js +10 -4
  11. package/dist/es2019/pm-plugins/menu-and-toolbar-experiences.js +261 -0
  12. package/dist/es2019/syncedBlockPlugin.js +19 -12
  13. package/dist/es2019/types/index.js +5 -2
  14. package/dist/es2019/ui/CreateSyncedBlockButton.js +2 -1
  15. package/dist/es2019/ui/CreateSyncedBlockDropdownItem.js +2 -1
  16. package/dist/es2019/ui/floating-toolbar.js +2 -1
  17. package/dist/esm/editor-commands/index.js +10 -4
  18. package/dist/esm/pm-plugins/menu-and-toolbar-experiences.js +260 -0
  19. package/dist/esm/syncedBlockPlugin.js +21 -12
  20. package/dist/esm/types/index.js +5 -2
  21. package/dist/esm/ui/CreateSyncedBlockButton.js +2 -1
  22. package/dist/esm/ui/CreateSyncedBlockDropdownItem.js +2 -1
  23. package/dist/esm/ui/floating-toolbar.js +2 -1
  24. package/dist/types/editor-commands/index.d.ts +3 -1
  25. package/dist/types/pm-plugins/menu-and-toolbar-experiences.d.ts +12 -0
  26. package/dist/types/types/index.d.ts +5 -14
  27. package/dist/types-ts4.5/editor-commands/index.d.ts +3 -1
  28. package/dist/types-ts4.5/pm-plugins/menu-and-toolbar-experiences.d.ts +12 -0
  29. package/dist/types-ts4.5/types/index.d.ts +5 -14
  30. package/package.json +4 -4
  31. package/dist/cjs/pm-plugins/experience-tracking/create-reference-experience.js +0 -113
  32. package/dist/cjs/pm-plugins/experience-tracking/create-source-experience.js +0 -169
  33. package/dist/cjs/pm-plugins/experience-tracking/delete-reference-experience.js +0 -175
  34. package/dist/cjs/pm-plugins/experience-tracking/delete-source-experience.js +0 -103
  35. package/dist/cjs/pm-plugins/experience-tracking/get-experience-tracking-plugins.js +0 -61
  36. package/dist/cjs/pm-plugins/experience-tracking/provider-only-experiences.js +0 -128
  37. package/dist/cjs/pm-plugins/utils/experience-tracking-utils.js +0 -85
  38. package/dist/es2019/pm-plugins/experience-tracking/create-reference-experience.js +0 -109
  39. package/dist/es2019/pm-plugins/experience-tracking/create-source-experience.js +0 -166
  40. package/dist/es2019/pm-plugins/experience-tracking/delete-reference-experience.js +0 -181
  41. package/dist/es2019/pm-plugins/experience-tracking/delete-source-experience.js +0 -98
  42. package/dist/es2019/pm-plugins/experience-tracking/get-experience-tracking-plugins.js +0 -46
  43. package/dist/es2019/pm-plugins/experience-tracking/provider-only-experiences.js +0 -127
  44. package/dist/es2019/pm-plugins/utils/experience-tracking-utils.js +0 -65
  45. package/dist/esm/pm-plugins/experience-tracking/create-reference-experience.js +0 -107
  46. package/dist/esm/pm-plugins/experience-tracking/create-source-experience.js +0 -163
  47. package/dist/esm/pm-plugins/experience-tracking/delete-reference-experience.js +0 -169
  48. package/dist/esm/pm-plugins/experience-tracking/delete-source-experience.js +0 -97
  49. package/dist/esm/pm-plugins/experience-tracking/get-experience-tracking-plugins.js +0 -55
  50. package/dist/esm/pm-plugins/experience-tracking/provider-only-experiences.js +0 -122
  51. package/dist/esm/pm-plugins/utils/experience-tracking-utils.js +0 -79
  52. package/dist/types/pm-plugins/experience-tracking/create-reference-experience.d.ts +0 -10
  53. package/dist/types/pm-plugins/experience-tracking/create-source-experience.d.ts +0 -10
  54. package/dist/types/pm-plugins/experience-tracking/delete-reference-experience.d.ts +0 -13
  55. package/dist/types/pm-plugins/experience-tracking/delete-source-experience.d.ts +0 -12
  56. package/dist/types/pm-plugins/experience-tracking/get-experience-tracking-plugins.d.ts +0 -5
  57. package/dist/types/pm-plugins/experience-tracking/provider-only-experiences.d.ts +0 -3
  58. package/dist/types/pm-plugins/utils/experience-tracking-utils.d.ts +0 -9
  59. package/dist/types-ts4.5/pm-plugins/experience-tracking/create-reference-experience.d.ts +0 -10
  60. package/dist/types-ts4.5/pm-plugins/experience-tracking/create-source-experience.d.ts +0 -10
  61. package/dist/types-ts4.5/pm-plugins/experience-tracking/delete-reference-experience.d.ts +0 -13
  62. package/dist/types-ts4.5/pm-plugins/experience-tracking/delete-source-experience.d.ts +0 -12
  63. package/dist/types-ts4.5/pm-plugins/experience-tracking/get-experience-tracking-plugins.d.ts +0 -5
  64. package/dist/types-ts4.5/pm-plugins/experience-tracking/provider-only-experiences.d.ts +0 -3
  65. package/dist/types-ts4.5/pm-plugins/utils/experience-tracking-utils.d.ts +0 -9
@@ -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))
@@ -9,6 +9,7 @@ import EditIcon from '@atlaskit/icon/core/edit';
9
9
  import { fg } from '@atlaskit/platform-feature-flags';
10
10
  import { copySyncedBlockReferenceToClipboard, editSyncedBlockSource, removeSyncedBlock } from '../editor-commands';
11
11
  import { findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
12
+ import { SYNCED_BLOCK_BUTTON_TEST_ID } from '../types';
12
13
  import { SyncedLocationDropdown } from './SyncedLocationDropdown';
13
14
  export const getToolbarConfig = (state, intl, api, syncBlockStore) => {
14
15
  var _api$decorations, _api$connectivity, _api$connectivity$sha;
@@ -49,7 +50,7 @@ export const getToolbarConfig = (state, intl, api, syncBlockStore) => {
49
50
  title: formatMessage(commonMessages.delete),
50
51
  onClick: removeSyncedBlock(api),
51
52
  icon: DeleteIcon,
52
- testId: fg('platform_synced_block_dogfooding') ? 'reference-synced-block-delete-button' : undefined,
53
+ testId: fg('platform_synced_block_dogfooding') ? SYNCED_BLOCK_BUTTON_TEST_ID.syncedBlockToolbarReferenceDelete : undefined,
53
54
  ...hoverDecorationProps(nodeType, akEditorSelectedNodeClassName)
54
55
  };
55
56
  items.push(deleteButton);
@@ -9,7 +9,8 @@ import { FLAG_ID } from '../types';
9
9
  export var createSyncedBlock = function createSyncedBlock(_ref) {
10
10
  var tr = _ref.tr,
11
11
  syncBlockStore = _ref.syncBlockStore,
12
- typeAheadInsert = _ref.typeAheadInsert;
12
+ typeAheadInsert = _ref.typeAheadInsert,
13
+ fireAnalyticsEvent = _ref.fireAnalyticsEvent;
13
14
  var _tr$doc$type$schema$n = tr.doc.type.schema.nodes,
14
15
  bodiedSyncBlock = _tr$doc$type$schema$n.bodiedSyncBlock,
15
16
  paragraph = _tr$doc$type$schema$n.paragraph;
@@ -35,9 +36,14 @@ export var createSyncedBlock = function createSyncedBlock(_ref) {
35
36
  var conversionInfo = canBeConvertedToSyncBlock(tr.selection);
36
37
  if (!conversionInfo) {
37
38
  if (fg('platform_synced_block_dogfooding')) {
38
- var _syncBlockStore$sourc;
39
- (_syncBlockStore$sourc = syncBlockStore.sourceManager.createExperience) === null || _syncBlockStore$sourc === void 0 || _syncBlockStore$sourc.failure({
40
- reason: 'Selection is not allowed to be converted to sync block'
39
+ fireAnalyticsEvent === null || fireAnalyticsEvent === void 0 || fireAnalyticsEvent({
40
+ action: ACTION.ERROR,
41
+ actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
42
+ actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE,
43
+ attributes: {
44
+ error: 'Content cannot be converted to sync block'
45
+ },
46
+ eventType: EVENT_TYPE.OPERATIONAL
41
47
  });
42
48
  }
43
49
  return false;
@@ -0,0 +1,260 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ import { bind } from 'bind-event-listener';
3
+ import { ACTION, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
4
+ import { Experience, EXPERIENCE_ID, ExperienceCheckDomMutation, ExperienceCheckTimeout, getNodeQuery, getPopupContainerFromEditorView, popupWithNestedElement } from '@atlaskit/editor-common/experiences';
5
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
6
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
7
+ import { SYNCED_BLOCK_BUTTON_TEST_ID } from '../types';
8
+ var TIMEOUT_DURATION = 30000;
9
+ var pluginKey = new PluginKey('syncedBlockMenuAndToolbarExperience');
10
+ var SYNCED_BLOCK_BUTTON_TEST_IDS = Object.values(SYNCED_BLOCK_BUTTON_TEST_ID);
11
+ var syncedBlockButtonIds = new Set(SYNCED_BLOCK_BUTTON_TEST_IDS);
12
+ var targetEl;
13
+ export var getMenuAndToolbarExperiencesPlugin = function getMenuAndToolbarExperiencesPlugin(_ref) {
14
+ var refs = _ref.refs,
15
+ dispatchAnalyticsEvent = _ref.dispatchAnalyticsEvent;
16
+ var popupsTargetEl;
17
+ var editorViewEl;
18
+ var getPopupsTarget = function getPopupsTarget() {
19
+ if (!popupsTargetEl) {
20
+ popupsTargetEl = refs.popupsMountPoint || refs.wrapperElement || getPopupContainerFromEditorView(editorViewEl);
21
+ }
22
+ return popupsTargetEl;
23
+ };
24
+ var createSourcePrimaryToolbarExperience = getCreateSourcePrimaryToolbarExperience({
25
+ refs: refs,
26
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent
27
+ });
28
+ var createSourceBlockMenuExperience = getCreateSourceBlockMenuExperience({
29
+ refs: refs,
30
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent
31
+ });
32
+ var createSourceQuickInsertMenuExperience = getCreateSourceQuickInsertMenuExperience({
33
+ refs: refs,
34
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent
35
+ });
36
+ var deleteReferenceSyncedBlockExperience = getDeleteReferenceSyncedBlockToolbarExperience({
37
+ refs: refs,
38
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent
39
+ });
40
+ var unbindClickListener = bind(document, {
41
+ type: 'click',
42
+ listener: function listener(event) {
43
+ var target = event.target;
44
+ if (!target) {
45
+ return;
46
+ }
47
+ var button = target.closest('button[data-testid]');
48
+ if (!button || !(button instanceof HTMLButtonElement)) {
49
+ return;
50
+ }
51
+ var testId = button.dataset.testid;
52
+ if (!isSyncedBlockButtonId(testId)) {
53
+ return;
54
+ }
55
+ handleButtonClick({
56
+ testId: testId,
57
+ createSourcePrimaryToolbarExperience: createSourcePrimaryToolbarExperience,
58
+ createSourceBlockMenuExperience: createSourceBlockMenuExperience,
59
+ createSourceQuickInsertMenuExperience: createSourceQuickInsertMenuExperience,
60
+ deleteReferenceSyncedBlockExperience: deleteReferenceSyncedBlockExperience
61
+ });
62
+ }
63
+ });
64
+ var unbindKeydownListener = bind(document, {
65
+ type: 'keydown',
66
+ listener: function listener(event) {
67
+ if (isEnterKey(event.key)) {
68
+ var typeaheadPopup = popupWithNestedElement(getPopupsTarget(), '.fabric-editor-typeahead');
69
+ if (!typeaheadPopup || !(typeaheadPopup instanceof HTMLElement)) {
70
+ return;
71
+ }
72
+ var firstItem = typeaheadPopup.querySelector('[role="option"]');
73
+ if (!firstItem || !(firstItem instanceof HTMLElement)) {
74
+ return;
75
+ }
76
+ var 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: function view(editorView) {
89
+ editorViewEl = editorView.dom;
90
+ return {
91
+ destroy: function 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
+ var getCreateSourcePrimaryToolbarExperience = function getCreateSourcePrimaryToolbarExperience(_ref2) {
112
+ var refs = _ref2.refs,
113
+ dispatchAnalyticsEvent = _ref2.dispatchAnalyticsEvent;
114
+ return new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
115
+ action: ACTION.SYNCED_BLOCK_CREATE,
116
+ actionSubjectId: ACTION_SUBJECT_ID.PRIMARY_TOOLBAR,
117
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
118
+ checks: [new ExperienceCheckTimeout({
119
+ durationMs: TIMEOUT_DURATION
120
+ }), syncedBlockAddedToDomCheck(refs)]
121
+ });
122
+ };
123
+ var getCreateSourceBlockMenuExperience = function getCreateSourceBlockMenuExperience(_ref3) {
124
+ var refs = _ref3.refs,
125
+ dispatchAnalyticsEvent = _ref3.dispatchAnalyticsEvent;
126
+ return new Experience(EXPERIENCE_ID.MENU_ACTION, {
127
+ action: ACTION.SYNCED_BLOCK_CREATE,
128
+ actionSubjectId: ACTION_SUBJECT_ID.BLOCK_MENU,
129
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
130
+ checks: [new ExperienceCheckTimeout({
131
+ durationMs: TIMEOUT_DURATION
132
+ }), syncedBlockAddedToDomCheck(refs)]
133
+ });
134
+ };
135
+ var getCreateSourceQuickInsertMenuExperience = function getCreateSourceQuickInsertMenuExperience(_ref4) {
136
+ var refs = _ref4.refs,
137
+ dispatchAnalyticsEvent = _ref4.dispatchAnalyticsEvent;
138
+ return new Experience(EXPERIENCE_ID.MENU_ACTION, {
139
+ action: ACTION.SYNCED_BLOCK_CREATE,
140
+ actionSubjectId: ACTION_SUBJECT_ID.QUICK_INSERT,
141
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
142
+ checks: [new ExperienceCheckTimeout({
143
+ durationMs: TIMEOUT_DURATION
144
+ }), syncedBlockAddedToDomCheck(refs)]
145
+ });
146
+ };
147
+ var getDeleteReferenceSyncedBlockToolbarExperience = function getDeleteReferenceSyncedBlockToolbarExperience(_ref5) {
148
+ var refs = _ref5.refs,
149
+ dispatchAnalyticsEvent = _ref5.dispatchAnalyticsEvent;
150
+ return new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
151
+ action: ACTION.REFERENCE_SYNCED_BLOCK_DELETE,
152
+ actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_TOOLBAR,
153
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
154
+ checks: [new ExperienceCheckTimeout({
155
+ durationMs: TIMEOUT_DURATION
156
+ }), referenceSyncBlockRemovedFromDomCheck(refs)]
157
+ });
158
+ };
159
+ var isSyncedBlockButtonId = function isSyncedBlockButtonId(value) {
160
+ return !!value && syncedBlockButtonIds.has(value);
161
+ };
162
+ var handleButtonClick = function handleButtonClick(_ref6) {
163
+ var testId = _ref6.testId,
164
+ createSourcePrimaryToolbarExperience = _ref6.createSourcePrimaryToolbarExperience,
165
+ createSourceBlockMenuExperience = _ref6.createSourceBlockMenuExperience,
166
+ createSourceQuickInsertMenuExperience = _ref6.createSourceQuickInsertMenuExperience,
167
+ deleteReferenceSyncedBlockExperience = _ref6.deleteReferenceSyncedBlockExperience;
168
+ switch (testId) {
169
+ case SYNCED_BLOCK_BUTTON_TEST_ID.primaryToolbarCreate:
170
+ createSourcePrimaryToolbarExperience.start();
171
+ break;
172
+ case SYNCED_BLOCK_BUTTON_TEST_ID.blockMenuCreate:
173
+ createSourceBlockMenuExperience.start();
174
+ break;
175
+ case SYNCED_BLOCK_BUTTON_TEST_ID.quickInsertCreate:
176
+ createSourceQuickInsertMenuExperience.start();
177
+ break;
178
+ case SYNCED_BLOCK_BUTTON_TEST_ID.syncedBlockToolbarReferenceDelete:
179
+ deleteReferenceSyncedBlockExperience.start();
180
+ break;
181
+ default:
182
+ {
183
+ // Exhaustiveness check: if a new SyncedBlockToolbarButtonId is added
184
+ // but not handled above, TypeScript will error here.
185
+ var _exhaustiveCheck = testId;
186
+ return _exhaustiveCheck;
187
+ }
188
+ }
189
+ };
190
+ var isEnterKey = function isEnterKey(key) {
191
+ return key === 'Enter';
192
+ };
193
+ var getTarget = function getTarget(containerElement) {
194
+ if (!targetEl) {
195
+ var element = containerElement === null || containerElement === void 0 ? void 0 : containerElement.querySelector('.ProseMirror');
196
+ if (!element || !(element instanceof HTMLElement)) {
197
+ return null;
198
+ }
199
+ targetEl = element;
200
+ }
201
+ return targetEl;
202
+ };
203
+ var syncedBlockAddedToDomCheck = function syncedBlockAddedToDomCheck(refs) {
204
+ return new ExperienceCheckDomMutation({
205
+ onDomMutation: function onDomMutation(_ref7) {
206
+ var mutations = _ref7.mutations;
207
+ if (mutations.some(isBodiedSyncBlockAddedInMutation)) {
208
+ return {
209
+ status: 'success'
210
+ };
211
+ }
212
+ return undefined;
213
+ },
214
+ observeConfig: function observeConfig() {
215
+ return {
216
+ target: getTarget(refs.containerElement),
217
+ options: {
218
+ childList: true
219
+ }
220
+ };
221
+ }
222
+ });
223
+ };
224
+ var isBodiedSyncBlockAddedInMutation = function isBodiedSyncBlockAddedInMutation(_ref8) {
225
+ var type = _ref8.type,
226
+ addedNodes = _ref8.addedNodes;
227
+ return type === 'childList' && _toConsumableArray(addedNodes).some(isBodiedSyncBlockWithinNode);
228
+ };
229
+ var isBodiedSyncBlockWithinNode = function isBodiedSyncBlockWithinNode(node) {
230
+ return getNodeQuery('[data-prosemirror-node-name="bodiedSyncBlock"]')(node);
231
+ };
232
+ var referenceSyncBlockRemovedFromDomCheck = function referenceSyncBlockRemovedFromDomCheck(refs) {
233
+ return new ExperienceCheckDomMutation({
234
+ onDomMutation: function onDomMutation(_ref9) {
235
+ var mutations = _ref9.mutations;
236
+ if (mutations.some(isSyncBlockRemovedInMutation)) {
237
+ return {
238
+ status: 'success'
239
+ };
240
+ }
241
+ return undefined;
242
+ },
243
+ observeConfig: function observeConfig() {
244
+ return {
245
+ target: getTarget(refs.containerElement),
246
+ options: {
247
+ childList: true
248
+ }
249
+ };
250
+ }
251
+ });
252
+ };
253
+ var isSyncBlockRemovedInMutation = function isSyncBlockRemovedInMutation(_ref0) {
254
+ var type = _ref0.type,
255
+ removedNodes = _ref0.removedNodes;
256
+ return type === 'childList' && _toConsumableArray(removedNodes).some(isSyncBlockWithinNode);
257
+ };
258
+ var isSyncBlockWithinNode = function isSyncBlockWithinNode(node) {
259
+ return getNodeQuery('[data-prosemirror-node-name="syncBlock"]')(node);
260
+ };