@atlaskit/editor-plugin-synced-block
Version:
SyncedBlock plugin for @atlaskit/editor-core
410 lines • 17.6 kB
JavaScript
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
import { bind } from 'bind-event-listener';
import { ACTION, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
import { Experience, EXPERIENCE_ID, ExperienceCheckDomMutation, ExperienceCheckTimeout, getNodeQuery, getPopupContainerFromEditorView, popupWithNestedElement, getSelectionAncestorDOM } from '@atlaskit/editor-common/experiences';
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
import { fg } from '@atlaskit/platform-feature-flags';
import { SYNCED_BLOCK_BUTTON_TEST_ID } from '../types';
var TIMEOUT_DURATION = 30000;
var pluginKey = new PluginKey('syncedBlockMenuAndToolbarExperience');
var SYNCED_BLOCK_BUTTON_TEST_IDS = Object.values(SYNCED_BLOCK_BUTTON_TEST_ID);
var syncedBlockButtonIds = new Set(SYNCED_BLOCK_BUTTON_TEST_IDS);
var targetEl;
export var getMenuAndToolbarExperiencesPlugin = function getMenuAndToolbarExperiencesPlugin(_ref) {
var refs = _ref.refs,
dispatchAnalyticsEvent = _ref.dispatchAnalyticsEvent;
var popupsTargetEl;
var editorViewRef = {
current: undefined
};
var getPopupsTarget = function getPopupsTarget() {
if (!popupsTargetEl) {
var _editorViewRef$curren;
popupsTargetEl = refs.popupsMountPoint || refs.wrapperElement || getPopupContainerFromEditorView(editorViewRef === null || editorViewRef === void 0 || (_editorViewRef$curren = editorViewRef.current) === null || _editorViewRef$curren === void 0 ? void 0 : _editorViewRef$curren.dom);
}
return popupsTargetEl;
};
var createSourcePrimaryToolbarExperience = new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
action: ACTION.SYNCED_BLOCK_CREATE,
actionSubjectId: ACTION_SUBJECT_ID.PRIMARY_TOOLBAR,
dispatchAnalyticsEvent: dispatchAnalyticsEvent,
checks: [new ExperienceCheckTimeout({
durationMs: TIMEOUT_DURATION
}), syncedBlockAddedToDomCheck(refs, editorViewRef)]
});
var createSourceBlockMenuExperience = new Experience(EXPERIENCE_ID.MENU_ACTION, {
action: ACTION.SYNCED_BLOCK_CREATE,
actionSubjectId: ACTION_SUBJECT_ID.BLOCK_MENU,
dispatchAnalyticsEvent: dispatchAnalyticsEvent,
checks: [new ExperienceCheckTimeout({
durationMs: TIMEOUT_DURATION
}), syncedBlockAddedToDomCheck(refs, editorViewRef)]
});
var createSourceQuickInsertMenuExperience = new Experience(EXPERIENCE_ID.MENU_ACTION, {
action: ACTION.SYNCED_BLOCK_CREATE,
actionSubjectId: ACTION_SUBJECT_ID.QUICK_INSERT,
dispatchAnalyticsEvent: dispatchAnalyticsEvent,
checks: [new ExperienceCheckTimeout({
durationMs: TIMEOUT_DURATION
}), syncedBlockAddedToDomCheck(refs, editorViewRef)]
});
var deleteReferenceSyncedBlockExperience = new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
action: ACTION.REFERENCE_SYNCED_BLOCK_DELETE,
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_TOOLBAR,
dispatchAnalyticsEvent: dispatchAnalyticsEvent,
checks: [new ExperienceCheckTimeout({
durationMs: TIMEOUT_DURATION
}), referenceSyncBlockRemovedFromDomCheck(refs, editorViewRef)]
});
var unsyncReferenceSyncedBlockExperience = new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
action: ACTION.REFERENCE_SYNCED_BLOCK_UNSYNC,
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_TOOLBAR,
dispatchAnalyticsEvent: dispatchAnalyticsEvent,
checks: [new ExperienceCheckTimeout({
durationMs: TIMEOUT_DURATION
}), referenceSyncBlockRemovedFromDomCheck(refs, editorViewRef)]
});
var unsyncSourceSyncedBlockExperience = new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
action: ACTION.SYNCED_BLOCK_UNSYNC,
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_TOOLBAR,
dispatchAnalyticsEvent: dispatchAnalyticsEvent,
checks: [new ExperienceCheckTimeout({
durationMs: TIMEOUT_DURATION
}), syncBlockDeleteConfirmationModalAddedCheck()]
});
var deleteSourceSyncedBlockExperience = new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
action: ACTION.SYNCED_BLOCK_DELETE,
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_TOOLBAR,
dispatchAnalyticsEvent: dispatchAnalyticsEvent,
checks: [new ExperienceCheckTimeout({
durationMs: TIMEOUT_DURATION
}), syncBlockDeleteConfirmationModalAddedCheck()]
});
var syncedLocationsExperience = new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
action: ACTION.SYNCED_BLOCK_VIEW_SYNCED_LOCATIONS,
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_TOOLBAR,
dispatchAnalyticsEvent: dispatchAnalyticsEvent,
checks: [new ExperienceCheckTimeout({
durationMs: TIMEOUT_DURATION
}), syncedLocationsDropdownOpenedCheck()]
});
var unbindClickListener = bind(document, {
type: 'click',
listener: function listener(event) {
var target = event.target;
if (!target) {
return;
}
var button = target.closest('button[data-testid]');
if (!button || !(button instanceof HTMLButtonElement)) {
return;
}
var testId = button.dataset.testid;
if (!isSyncedBlockButtonId(testId)) {
return;
}
if (button.disabled) {
return;
}
handleButtonClick({
testId: testId,
button: button,
createSourcePrimaryToolbarExperience: createSourcePrimaryToolbarExperience,
createSourceBlockMenuExperience: createSourceBlockMenuExperience,
createSourceQuickInsertMenuExperience: createSourceQuickInsertMenuExperience,
deleteReferenceSyncedBlockExperience: deleteReferenceSyncedBlockExperience,
unsyncReferenceSyncedBlockExperience: unsyncReferenceSyncedBlockExperience,
unsyncSourceSyncedBlockExperience: unsyncSourceSyncedBlockExperience,
deleteSourceSyncedBlockExperience: deleteSourceSyncedBlockExperience,
syncedLocationsExperience: syncedLocationsExperience
});
},
options: {
capture: true
}
});
var unbindKeydownListener = bind(document, {
type: 'keydown',
listener: function listener(event) {
if (isEnterKey(event.key)) {
var typeaheadPopup = popupWithNestedElement(getPopupsTarget(), '.fabric-editor-typeahead');
if (!typeaheadPopup || !(typeaheadPopup instanceof HTMLElement)) {
return;
}
var targetElement = fg('platform_synced_block_fix_experience_tracking') ? typeaheadPopup.querySelector('[role="option"][aria-selected="true"]') : typeaheadPopup.querySelector('[role="option"]');
if (!targetElement || !(targetElement instanceof HTMLElement)) {
return;
}
var testId = targetElement.dataset.testid;
if (testId === SYNCED_BLOCK_BUTTON_TEST_ID.quickInsertCreate) {
createSourceQuickInsertMenuExperience.start();
}
}
},
options: {
capture: true
}
});
return new SafePlugin({
key: pluginKey,
view: function view(_view) {
editorViewRef.current = _view;
return {
destroy: function destroy() {
createSourcePrimaryToolbarExperience.abort({
reason: 'editorDestroyed'
});
createSourceBlockMenuExperience.abort({
reason: 'editorDestroyed'
});
createSourceQuickInsertMenuExperience.abort({
reason: 'editorDestroyed'
});
deleteReferenceSyncedBlockExperience.abort({
reason: 'editorDestroyed'
});
deleteSourceSyncedBlockExperience === null || deleteSourceSyncedBlockExperience === void 0 || deleteSourceSyncedBlockExperience.abort({
reason: 'editorDestroyed'
});
unsyncReferenceSyncedBlockExperience === null || unsyncReferenceSyncedBlockExperience === void 0 || unsyncReferenceSyncedBlockExperience.abort({
reason: 'editorDestroyed'
});
unsyncSourceSyncedBlockExperience === null || unsyncSourceSyncedBlockExperience === void 0 || unsyncSourceSyncedBlockExperience.abort({
reason: 'editorDestroyed'
});
syncedLocationsExperience === null || syncedLocationsExperience === void 0 || syncedLocationsExperience.abort({
reason: 'editorDestroyed'
});
unbindClickListener();
unbindKeydownListener();
}
};
}
});
};
var isSyncedBlockButtonId = function isSyncedBlockButtonId(value) {
return !!value && syncedBlockButtonIds.has(value);
};
var handleButtonClick = function handleButtonClick(_ref2) {
var testId = _ref2.testId,
button = _ref2.button,
createSourcePrimaryToolbarExperience = _ref2.createSourcePrimaryToolbarExperience,
createSourceBlockMenuExperience = _ref2.createSourceBlockMenuExperience,
createSourceQuickInsertMenuExperience = _ref2.createSourceQuickInsertMenuExperience,
deleteReferenceSyncedBlockExperience = _ref2.deleteReferenceSyncedBlockExperience,
unsyncReferenceSyncedBlockExperience = _ref2.unsyncReferenceSyncedBlockExperience,
unsyncSourceSyncedBlockExperience = _ref2.unsyncSourceSyncedBlockExperience,
deleteSourceSyncedBlockExperience = _ref2.deleteSourceSyncedBlockExperience,
syncedLocationsExperience = _ref2.syncedLocationsExperience;
switch (testId) {
case SYNCED_BLOCK_BUTTON_TEST_ID.primaryToolbarCreate:
createSourcePrimaryToolbarExperience.start({
forceRestart: true
});
break;
case SYNCED_BLOCK_BUTTON_TEST_ID.blockMenuCreate:
createSourceBlockMenuExperience.start({
forceRestart: true
});
break;
case SYNCED_BLOCK_BUTTON_TEST_ID.quickInsertCreate:
createSourceQuickInsertMenuExperience.start({
forceRestart: true
});
break;
case SYNCED_BLOCK_BUTTON_TEST_ID.syncedBlockToolbarReferenceDelete:
deleteReferenceSyncedBlockExperience.start({
forceRestart: true
});
break;
case SYNCED_BLOCK_BUTTON_TEST_ID.syncedBlockToolbarReferenceUnsync:
unsyncReferenceSyncedBlockExperience === null || unsyncReferenceSyncedBlockExperience === void 0 || unsyncReferenceSyncedBlockExperience.start({
forceRestart: true
});
break;
case SYNCED_BLOCK_BUTTON_TEST_ID.syncedBlockToolbarSourceUnsync:
unsyncSourceSyncedBlockExperience === null || unsyncSourceSyncedBlockExperience === void 0 || unsyncSourceSyncedBlockExperience.start({
forceRestart: true
});
break;
case SYNCED_BLOCK_BUTTON_TEST_ID.syncedBlockToolbarSourceDelete:
deleteSourceSyncedBlockExperience === null || deleteSourceSyncedBlockExperience === void 0 || deleteSourceSyncedBlockExperience.start({
forceRestart: true
});
break;
case SYNCED_BLOCK_BUTTON_TEST_ID.syncedBlockToolbarSyncedLocationsTrigger:
// Only track when opening the dropdown
if (button.getAttribute('aria-pressed') === 'false') {
syncedLocationsExperience === null || syncedLocationsExperience === void 0 || syncedLocationsExperience.start({
forceRestart: true
});
}
break;
default:
{
// Exhaustiveness check: if a new SyncedBlockToolbarButtonId is added
// but not handled above, TypeScript will error here.
var _exhaustiveCheck = testId;
return _exhaustiveCheck;
}
}
};
var isEnterKey = function isEnterKey(key) {
return key === 'Enter';
};
var getTarget = function getTarget(containerElement) {
if (!targetEl) {
var element = containerElement === null || containerElement === void 0 ? void 0 : containerElement.querySelector('.ProseMirror');
if (!element || !(element instanceof HTMLElement)) {
return null;
}
targetEl = element;
}
return targetEl;
};
var syncedBlockAddedToDomCheck = function syncedBlockAddedToDomCheck(refs, editorViewRef) {
return new ExperienceCheckDomMutation({
onDomMutation: function onDomMutation(_ref3) {
var mutations = _ref3.mutations;
if (mutations.some(isBodiedSyncBlockAddedInMutation)) {
return {
status: 'success'
};
}
return undefined;
},
observeConfig: function observeConfig() {
var _editorViewRef$curren2;
return [{
target: fg('platform_synced_block_fix_experience_tracking') ? editorViewRef === null || editorViewRef === void 0 || (_editorViewRef$curren2 = editorViewRef.current) === null || _editorViewRef$curren2 === void 0 ? void 0 : _editorViewRef$curren2.dom : getTarget(refs.containerElement),
options: {
childList: true
}
}].concat(_toConsumableArray(fg('platform_synced_block_fix_experience_tracking') ? [{
target: getSelectionAncestorDOM(editorViewRef === null || editorViewRef === void 0 ? void 0 : editorViewRef.current),
options: {
childList: true,
subtree: true
}
}] : []));
}
});
};
var isBodiedSyncBlockAddedInMutation = function isBodiedSyncBlockAddedInMutation(_ref4) {
var type = _ref4.type,
addedNodes = _ref4.addedNodes;
return type === 'childList' && _toConsumableArray(addedNodes).some(isBodiedSyncBlockWithinNode);
};
var isBodiedSyncBlockWithinNode = function isBodiedSyncBlockWithinNode(node) {
return getNodeQuery('[data-prosemirror-node-name="bodiedSyncBlock"]')(node);
};
var referenceSyncBlockRemovedFromDomCheck = function referenceSyncBlockRemovedFromDomCheck(refs, editorViewRef) {
return new ExperienceCheckDomMutation({
onDomMutation: function onDomMutation(_ref5) {
var mutations = _ref5.mutations;
if (mutations.some(isSyncBlockRemovedInMutation)) {
return {
status: 'success'
};
}
return undefined;
},
observeConfig: function observeConfig() {
var _editorViewRef$curren3;
return [{
target: fg('platform_synced_block_fix_experience_tracking') ? editorViewRef === null || editorViewRef === void 0 || (_editorViewRef$curren3 = editorViewRef.current) === null || _editorViewRef$curren3 === void 0 ? void 0 : _editorViewRef$curren3.dom : getTarget(refs.containerElement),
options: {
childList: true
}
}].concat(_toConsumableArray(fg('platform_synced_block_fix_experience_tracking') ? [{
target: getSelectionAncestorDOM(editorViewRef === null || editorViewRef === void 0 ? void 0 : editorViewRef.current),
options: {
childList: true,
subtree: true
}
}] : []));
}
});
};
var isSyncBlockRemovedInMutation = function isSyncBlockRemovedInMutation(_ref6) {
var type = _ref6.type,
removedNodes = _ref6.removedNodes;
return type === 'childList' && _toConsumableArray(removedNodes).some(isSyncBlockWithinNode);
};
var isSyncBlockWithinNode = function isSyncBlockWithinNode(node) {
return getNodeQuery('[data-prosemirror-node-name="syncBlock"]')(node);
};
var syncBlockDeleteConfirmationModalAddedCheck = function syncBlockDeleteConfirmationModalAddedCheck() {
return new ExperienceCheckDomMutation({
onDomMutation: function onDomMutation(_ref7) {
var mutations = _ref7.mutations;
if (mutations.some(isDeleteConfirmationModalAddedInMutation)) {
return {
status: 'success'
};
}
return undefined;
},
observeConfig: function observeConfig() {
return {
target: document.body,
options: {
childList: true,
subtree: true
}
};
}
});
};
var isDeleteConfirmationModalAddedInMutation = function isDeleteConfirmationModalAddedInMutation(_ref8) {
var type = _ref8.type,
addedNodes = _ref8.addedNodes;
return type === 'childList' && _toConsumableArray(addedNodes).some(isDeleteConfirmationModalWithinNode);
};
var isDeleteConfirmationModalWithinNode = function isDeleteConfirmationModalWithinNode(node) {
return getNodeQuery('[data-testid="sync-block-delete-confirmation"]')(node);
};
var syncedLocationsDropdownOpenedCheck = function syncedLocationsDropdownOpenedCheck() {
return new ExperienceCheckDomMutation({
onDomMutation: function onDomMutation(_ref9) {
var mutations = _ref9.mutations;
if (mutations.some(isSyncedLocationsDropdownErrorInMutation)) {
return {
status: 'failure'
};
}
if (mutations.some(isSyncedLocationsDropdownAddedInMutation)) {
return {
status: 'success'
};
}
return undefined;
},
observeConfig: function observeConfig() {
return {
target: document.body,
options: {
childList: true,
subtree: true
}
};
}
});
};
var isSyncedLocationsDropdownAddedInMutation = function isSyncedLocationsDropdownAddedInMutation(_ref0) {
var type = _ref0.type,
addedNodes = _ref0.addedNodes;
return type === 'childList' && _toConsumableArray(addedNodes).some(isSyncedLocationsDropdownWithinNode);
};
var isSyncedLocationsDropdownErrorInMutation = function isSyncedLocationsDropdownErrorInMutation(_ref1) {
var type = _ref1.type,
addedNodes = _ref1.addedNodes;
return type === 'childList' && _toConsumableArray(addedNodes).some(isSyncedLocationsDropdownErrorWithinNode);
};
var isSyncedLocationsDropdownWithinNode = function isSyncedLocationsDropdownWithinNode(node) {
return !!(getNodeQuery('[data-testid="synced-locations-dropdown-content"]')(node) || getNodeQuery('[data-testid="synced-locations-dropdown-content-no-results"]')(node));
};
var isSyncedLocationsDropdownErrorWithinNode = function isSyncedLocationsDropdownErrorWithinNode(node) {
return !!getNodeQuery('[data-testid="synced-locations-dropdown-content-error"]')(node);
};