@atlaskit/editor-plugin-insert-block 8.4.4 → 8.4.5
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.
- package/CHANGELOG.md +8 -0
- package/dist/cjs/insertBlockPlugin.js +17 -0
- package/dist/cjs/pm-plugins/experiences/toolbar-action-experiences.js +183 -0
- package/dist/cjs/pm-plugins/experiences/toolbar-experience-utils.js +409 -0
- package/dist/cjs/ui/toolbar-components/EmojiButton.js +3 -1
- package/dist/cjs/ui/toolbar-components/ImageButton.js +2 -1
- package/dist/cjs/ui/toolbar-components/InsertButton.js +2 -1
- package/dist/cjs/ui/toolbar-components/LayoutButton.js +2 -1
- package/dist/cjs/ui/toolbar-components/MediaButton.js +3 -1
- package/dist/cjs/ui/toolbar-components/MentionButton.js +3 -1
- package/dist/cjs/ui/toolbar-components/TableButton.js +1 -1
- package/dist/cjs/ui/toolbar-components/TableSizePicker.js +3 -1
- package/dist/cjs/ui/toolbar-components/TaskListButton.js +2 -1
- package/dist/es2019/insertBlockPlugin.js +15 -0
- package/dist/es2019/pm-plugins/experiences/toolbar-action-experiences.js +173 -0
- package/dist/es2019/pm-plugins/experiences/toolbar-experience-utils.js +279 -0
- package/dist/es2019/ui/toolbar-components/EmojiButton.js +3 -1
- package/dist/es2019/ui/toolbar-components/ImageButton.js +3 -2
- package/dist/es2019/ui/toolbar-components/InsertButton.js +3 -2
- package/dist/es2019/ui/toolbar-components/LayoutButton.js +3 -2
- package/dist/es2019/ui/toolbar-components/MediaButton.js +3 -1
- package/dist/es2019/ui/toolbar-components/MentionButton.js +3 -1
- package/dist/es2019/ui/toolbar-components/TableButton.js +2 -2
- package/dist/es2019/ui/toolbar-components/TableSizePicker.js +3 -1
- package/dist/es2019/ui/toolbar-components/TaskListButton.js +3 -2
- package/dist/esm/insertBlockPlugin.js +17 -0
- package/dist/esm/pm-plugins/experiences/toolbar-action-experiences.js +177 -0
- package/dist/esm/pm-plugins/experiences/toolbar-experience-utils.js +403 -0
- package/dist/esm/ui/toolbar-components/EmojiButton.js +3 -1
- package/dist/esm/ui/toolbar-components/ImageButton.js +3 -2
- package/dist/esm/ui/toolbar-components/InsertButton.js +3 -2
- package/dist/esm/ui/toolbar-components/LayoutButton.js +3 -2
- package/dist/esm/ui/toolbar-components/MediaButton.js +3 -1
- package/dist/esm/ui/toolbar-components/MentionButton.js +3 -1
- package/dist/esm/ui/toolbar-components/TableButton.js +2 -2
- package/dist/esm/ui/toolbar-components/TableSizePicker.js +3 -1
- package/dist/esm/ui/toolbar-components/TaskListButton.js +3 -2
- package/dist/types/pm-plugins/experiences/toolbar-action-experiences.d.ts +10 -0
- package/dist/types/pm-plugins/experiences/toolbar-experience-utils.d.ts +57 -0
- package/dist/types-ts4.5/pm-plugins/experiences/toolbar-action-experiences.d.ts +10 -0
- package/dist/types-ts4.5/pm-plugins/experiences/toolbar-experience-utils.d.ts +57 -0
- package/package.json +5 -1
|
@@ -35,6 +35,7 @@ var LayoutButton = exports.LayoutButton = function LayoutButton(_ref) {
|
|
|
35
35
|
label: formatMessage(_messages.toolbarInsertBlockMessages.columns),
|
|
36
36
|
size: "small"
|
|
37
37
|
}),
|
|
38
|
-
onClick: onClick
|
|
38
|
+
onClick: onClick,
|
|
39
|
+
testId: _toolbar.TOOLBAR_BUTTON_TEST_ID.LAYOUT
|
|
39
40
|
}));
|
|
40
41
|
};
|
|
@@ -10,6 +10,7 @@ var _reactIntlNext = require("react-intl-next");
|
|
|
10
10
|
var _analytics = require("@atlaskit/editor-common/analytics");
|
|
11
11
|
var _hooks = require("@atlaskit/editor-common/hooks");
|
|
12
12
|
var _messages = require("@atlaskit/editor-common/messages");
|
|
13
|
+
var _toolbar = require("@atlaskit/editor-common/toolbar");
|
|
13
14
|
var _editorPluginConnectivity = require("@atlaskit/editor-plugin-connectivity");
|
|
14
15
|
var _editorToolbar = require("@atlaskit/editor-toolbar");
|
|
15
16
|
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); }
|
|
@@ -67,6 +68,7 @@ var MediaButton = exports.MediaButton = function MediaButton(_ref) {
|
|
|
67
68
|
}),
|
|
68
69
|
onClick: onClick,
|
|
69
70
|
ref: mediaButtonRef,
|
|
70
|
-
isDisabled: isOffline || !allowsUploads
|
|
71
|
+
isDisabled: isOffline || !allowsUploads,
|
|
72
|
+
testId: _toolbar.TOOLBAR_BUTTON_TEST_ID.MEDIA
|
|
71
73
|
}));
|
|
72
74
|
};
|
|
@@ -11,6 +11,7 @@ var _analytics = require("@atlaskit/editor-common/analytics");
|
|
|
11
11
|
var _hooks = require("@atlaskit/editor-common/hooks");
|
|
12
12
|
var _keymaps = require("@atlaskit/editor-common/keymaps");
|
|
13
13
|
var _messages = require("@atlaskit/editor-common/messages");
|
|
14
|
+
var _toolbar = require("@atlaskit/editor-common/toolbar");
|
|
14
15
|
var _editorToolbar = require("@atlaskit/editor-toolbar");
|
|
15
16
|
var MentionButton = exports.MentionButton = function MentionButton(_ref) {
|
|
16
17
|
var api = _ref.api;
|
|
@@ -46,6 +47,7 @@ var MentionButton = exports.MentionButton = function MentionButton(_ref) {
|
|
|
46
47
|
}),
|
|
47
48
|
onClick: onClick,
|
|
48
49
|
ariaKeyshortcuts: "Shift+2 Space",
|
|
49
|
-
isDisabled: !canInsertMention || !mentionProvider || !isTypeAheadAllowed
|
|
50
|
+
isDisabled: !canInsertMention || !mentionProvider || !isTypeAheadAllowed,
|
|
51
|
+
testId: _toolbar.TOOLBAR_BUTTON_TEST_ID.MENTION
|
|
50
52
|
}));
|
|
51
53
|
};
|
|
@@ -8,6 +8,7 @@ exports.TableSizePicker = void 0;
|
|
|
8
8
|
var _react = _interopRequireWildcard(require("react"));
|
|
9
9
|
var _reactIntlNext = require("react-intl-next");
|
|
10
10
|
var _messages = require("@atlaskit/editor-common/messages");
|
|
11
|
+
var _toolbar = require("@atlaskit/editor-common/toolbar");
|
|
11
12
|
var _editorToolbar = require("@atlaskit/editor-toolbar");
|
|
12
13
|
var _useTableSelectorPopup = require("./hooks/useTableSelectorPopup");
|
|
13
14
|
var _TableSelectorPopupWrapper = require("./popups/TableSelectorPopupWrapper");
|
|
@@ -54,6 +55,7 @@ var TableSizePicker = exports.TableSizePicker = function TableSizePicker(_ref) {
|
|
|
54
55
|
}),
|
|
55
56
|
onClick: onClick,
|
|
56
57
|
isSelected: tableSelectorPopup.isOpen,
|
|
57
|
-
ref: tableSizePickerRef
|
|
58
|
+
ref: tableSizePickerRef,
|
|
59
|
+
testId: _toolbar.TOOLBAR_BUTTON_TEST_ID.TABLE_SELECTOR
|
|
58
60
|
})));
|
|
59
61
|
};
|
|
@@ -9,6 +9,7 @@ import { BLOCK_QUOTE, CODE_BLOCK, PANEL } from '@atlaskit/editor-plugin-block-ty
|
|
|
9
9
|
import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
|
|
10
10
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
11
11
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
12
|
+
import { getToolbarActionExperiencesPlugin } from './pm-plugins/experiences/toolbar-action-experiences';
|
|
12
13
|
import { toggleInsertBlockPmKey, toggleInsertBlockPmPlugin } from './pm-plugins/toggleInsertBlock';
|
|
13
14
|
import { getToolbarComponents } from './ui/toolbar-components';
|
|
14
15
|
// Ignored via go/ees005
|
|
@@ -92,6 +93,7 @@ export const insertBlockPlugin = ({
|
|
|
92
93
|
api
|
|
93
94
|
}) => {
|
|
94
95
|
const isToolbarAIFCEnabled = Boolean(api === null || api === void 0 ? void 0 : api.toolbar);
|
|
96
|
+
const refs = {};
|
|
95
97
|
const primaryToolbarComponent = ({
|
|
96
98
|
editorView,
|
|
97
99
|
editorActions,
|
|
@@ -105,6 +107,7 @@ export const insertBlockPlugin = ({
|
|
|
105
107
|
isToolbarReducedSpacing,
|
|
106
108
|
isLastItem
|
|
107
109
|
}) => {
|
|
110
|
+
refs.popupsMountPoint = popupsMountPoint || undefined;
|
|
108
111
|
const renderNode = providers => {
|
|
109
112
|
if (!editorView) {
|
|
110
113
|
return null;
|
|
@@ -210,6 +213,18 @@ export const insertBlockPlugin = ({
|
|
|
210
213
|
name: 'toggleInsertBlockPmPlugin',
|
|
211
214
|
plugin: () => toggleInsertBlockPmPlugin()
|
|
212
215
|
});
|
|
216
|
+
if (fg('platform_editor_experience_tracking_toolbar_button')) {
|
|
217
|
+
plugins.push({
|
|
218
|
+
name: 'toolbarActionExperiences',
|
|
219
|
+
plugin: () => getToolbarActionExperiencesPlugin({
|
|
220
|
+
refs,
|
|
221
|
+
dispatchAnalyticsEvent: payload => {
|
|
222
|
+
var _api$analytics, _api$analytics$action;
|
|
223
|
+
return api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : (_api$analytics$action = _api$analytics.actions) === null || _api$analytics$action === void 0 ? void 0 : _api$analytics$action.fireAnalyticsEvent(payload);
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
});
|
|
227
|
+
}
|
|
213
228
|
return plugins;
|
|
214
229
|
},
|
|
215
230
|
pluginsOptions: {},
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { bind } from 'bind-event-listener';
|
|
2
|
+
import { getDocument } from '@atlaskit/browser-apis';
|
|
3
|
+
import { Experience, EXPERIENCE_ID, ExperienceCheckDomMutation, ExperienceCheckTimeout, getPopupContainerFromEditorView } from '@atlaskit/editor-common/experiences';
|
|
4
|
+
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
5
|
+
import { TOOLBAR_BUTTON_TEST_ID } from '@atlaskit/editor-common/toolbar';
|
|
6
|
+
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
7
|
+
import { ExperienceCheckPopupMutation, getParentDOMAtSelection, handleEditorNodeInsertDomMutation, handleTypeAheadOpenDomMutation, isToolbarButtonClick } from './toolbar-experience-utils';
|
|
8
|
+
const pluginKey = new PluginKey('toolbarActionExperiences');
|
|
9
|
+
const TIMEOUT_DURATION = 1000;
|
|
10
|
+
const PRIMARY_TOOLBAR = 'primaryToolbar';
|
|
11
|
+
const ABORT_REASON = {
|
|
12
|
+
USER_CANCELED: 'userCanceled',
|
|
13
|
+
EDITOR_DESTROYED: 'editorDestroyed'
|
|
14
|
+
};
|
|
15
|
+
export const getToolbarActionExperiencesPlugin = ({
|
|
16
|
+
refs,
|
|
17
|
+
dispatchAnalyticsEvent
|
|
18
|
+
}) => {
|
|
19
|
+
let editorView;
|
|
20
|
+
let popupTargetEl;
|
|
21
|
+
const getPopupsTarget = () => {
|
|
22
|
+
if (!popupTargetEl) {
|
|
23
|
+
var _editorView;
|
|
24
|
+
popupTargetEl = refs.popupsMountPoint || getPopupContainerFromEditorView((_editorView = editorView) === null || _editorView === void 0 ? void 0 : _editorView.dom);
|
|
25
|
+
}
|
|
26
|
+
return popupTargetEl;
|
|
27
|
+
};
|
|
28
|
+
const getEditorDom = () => {
|
|
29
|
+
var _editorView2;
|
|
30
|
+
if (((_editorView2 = editorView) === null || _editorView2 === void 0 ? void 0 : _editorView2.dom) instanceof HTMLElement) {
|
|
31
|
+
return editorView.dom;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
};
|
|
35
|
+
const narrowParentObserveConfig = () => {
|
|
36
|
+
var _getParentDOMAtSelect;
|
|
37
|
+
return {
|
|
38
|
+
target: (_getParentDOMAtSelect = getParentDOMAtSelection(editorView)) !== null && _getParentDOMAtSelect !== void 0 ? _getParentDOMAtSelect : getEditorDom(),
|
|
39
|
+
options: {
|
|
40
|
+
childList: true
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
const rootObserveConfig = () => ({
|
|
45
|
+
target: getEditorDom(),
|
|
46
|
+
options: {
|
|
47
|
+
childList: true
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const createNodeInsertExperience = action => new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
|
|
51
|
+
action,
|
|
52
|
+
actionSubjectId: PRIMARY_TOOLBAR,
|
|
53
|
+
dispatchAnalyticsEvent,
|
|
54
|
+
checks: [new ExperienceCheckTimeout({
|
|
55
|
+
durationMs: TIMEOUT_DURATION
|
|
56
|
+
}), new ExperienceCheckDomMutation({
|
|
57
|
+
onDomMutation: handleEditorNodeInsertDomMutation,
|
|
58
|
+
observeConfig: narrowParentObserveConfig
|
|
59
|
+
}), new ExperienceCheckDomMutation({
|
|
60
|
+
onDomMutation: handleEditorNodeInsertDomMutation,
|
|
61
|
+
observeConfig: rootObserveConfig
|
|
62
|
+
})]
|
|
63
|
+
});
|
|
64
|
+
const createPopupExperience = (action, popupSelector) => new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
|
|
65
|
+
action,
|
|
66
|
+
actionSubjectId: PRIMARY_TOOLBAR,
|
|
67
|
+
dispatchAnalyticsEvent,
|
|
68
|
+
checks: [new ExperienceCheckTimeout({
|
|
69
|
+
durationMs: TIMEOUT_DURATION
|
|
70
|
+
}), new ExperienceCheckPopupMutation(popupSelector, getPopupsTarget, getEditorDom)]
|
|
71
|
+
});
|
|
72
|
+
const experienceButtonMappings = [{
|
|
73
|
+
experience: createPopupExperience('emoji', '[data-emoji-picker-container]'),
|
|
74
|
+
buttonTestId: TOOLBAR_BUTTON_TEST_ID.EMOJI
|
|
75
|
+
}, {
|
|
76
|
+
experience: createPopupExperience('media', '[id="local-media-upload-button"], [data-testid="media-picker-file-input"]'),
|
|
77
|
+
buttonTestId: TOOLBAR_BUTTON_TEST_ID.MEDIA
|
|
78
|
+
}, {
|
|
79
|
+
experience: new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
|
|
80
|
+
action: 'mention',
|
|
81
|
+
actionSubjectId: PRIMARY_TOOLBAR,
|
|
82
|
+
dispatchAnalyticsEvent,
|
|
83
|
+
checks: [new ExperienceCheckTimeout({
|
|
84
|
+
durationMs: TIMEOUT_DURATION
|
|
85
|
+
}), new ExperienceCheckDomMutation({
|
|
86
|
+
onDomMutation: handleTypeAheadOpenDomMutation,
|
|
87
|
+
observeConfig: narrowParentObserveConfig
|
|
88
|
+
})]
|
|
89
|
+
}),
|
|
90
|
+
buttonTestId: TOOLBAR_BUTTON_TEST_ID.MENTION
|
|
91
|
+
}, {
|
|
92
|
+
experience: createNodeInsertExperience('table'),
|
|
93
|
+
buttonTestId: TOOLBAR_BUTTON_TEST_ID.TABLE
|
|
94
|
+
}, {
|
|
95
|
+
experience: createPopupExperience('tableSelector', '[aria-label*="table size"], [data-testid*="table-selector"]'),
|
|
96
|
+
buttonTestId: TOOLBAR_BUTTON_TEST_ID.TABLE_SELECTOR
|
|
97
|
+
}, {
|
|
98
|
+
experience: createNodeInsertExperience('layout'),
|
|
99
|
+
buttonTestId: TOOLBAR_BUTTON_TEST_ID.LAYOUT
|
|
100
|
+
}, {
|
|
101
|
+
experience: createPopupExperience('image', '[id="local-media-upload-button"], [data-testid="media-picker-file-input"]'),
|
|
102
|
+
buttonTestId: TOOLBAR_BUTTON_TEST_ID.IMAGE
|
|
103
|
+
}, {
|
|
104
|
+
experience: createNodeInsertExperience('action'),
|
|
105
|
+
buttonTestId: TOOLBAR_BUTTON_TEST_ID.TASK_LIST
|
|
106
|
+
}];
|
|
107
|
+
const handleToolbarButtonClick = target => {
|
|
108
|
+
for (const {
|
|
109
|
+
experience,
|
|
110
|
+
buttonTestId
|
|
111
|
+
} of experienceButtonMappings) {
|
|
112
|
+
if (isToolbarButtonClick(target, buttonTestId)) {
|
|
113
|
+
experience.start({
|
|
114
|
+
forceRestart: true
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const abortAllExperiences = reason => {
|
|
121
|
+
for (const {
|
|
122
|
+
experience
|
|
123
|
+
} of experienceButtonMappings) {
|
|
124
|
+
experience.abort({
|
|
125
|
+
reason
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const doc = getDocument();
|
|
130
|
+
if (!doc) {
|
|
131
|
+
return new SafePlugin({
|
|
132
|
+
key: pluginKey
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const unbindClickListener = bind(doc, {
|
|
136
|
+
type: 'click',
|
|
137
|
+
listener: event => {
|
|
138
|
+
const target = event.target;
|
|
139
|
+
if (target instanceof HTMLElement) {
|
|
140
|
+
handleToolbarButtonClick(target);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
options: {
|
|
144
|
+
capture: true
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
const unbindKeydownListener = bind(doc, {
|
|
148
|
+
type: 'keydown',
|
|
149
|
+
listener: event => {
|
|
150
|
+
if (event.key === 'Escape') {
|
|
151
|
+
abortAllExperiences(ABORT_REASON.USER_CANCELED);
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
options: {
|
|
155
|
+
capture: true
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
return new SafePlugin({
|
|
159
|
+
key: pluginKey,
|
|
160
|
+
view: view => {
|
|
161
|
+
editorView = view;
|
|
162
|
+
return {
|
|
163
|
+
destroy: () => {
|
|
164
|
+
abortAllExperiences(ABORT_REASON.EDITOR_DESTROYED);
|
|
165
|
+
editorView = undefined;
|
|
166
|
+
popupTargetEl = undefined;
|
|
167
|
+
unbindClickListener();
|
|
168
|
+
unbindKeydownListener();
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
};
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import { getDocument } from '@atlaskit/browser-apis';
|
|
3
|
+
import { EXPERIENCE_FAILURE_REASON, popupWithNestedElement } from '@atlaskit/editor-common/experiences';
|
|
4
|
+
/**
|
|
5
|
+
* DOM marker selectors for node types inserted via toolbar actions.
|
|
6
|
+
* Matches outermost wrapper elements set synchronously by ReactNodeView
|
|
7
|
+
* (`{nodeTypeName}View-content-wrap`) or schema `toDOM` attributes.
|
|
8
|
+
*/
|
|
9
|
+
export const NODE_INSERT_MARKERS = {
|
|
10
|
+
TABLE: '.tableView-content-wrap',
|
|
11
|
+
LAYOUT: '.layoutSectionView-content-wrap',
|
|
12
|
+
LAYOUT_COLUMN: '.layoutColumnView-content-wrap',
|
|
13
|
+
TASK_LIST: '[data-node-type="actionList"]',
|
|
14
|
+
TASK_ITEM: '.taskItemView-content-wrap'
|
|
15
|
+
};
|
|
16
|
+
const COMBINED_NODE_INSERT_SELECTOR = [NODE_INSERT_MARKERS.TABLE, NODE_INSERT_MARKERS.LAYOUT, NODE_INSERT_MARKERS.LAYOUT_COLUMN, NODE_INSERT_MARKERS.TASK_LIST, NODE_INSERT_MARKERS.TASK_ITEM].join(', ');
|
|
17
|
+
export const isToolbarButtonClick = (target, testId) => {
|
|
18
|
+
const button = target.closest(`button[data-testid="${testId}"]`);
|
|
19
|
+
if (!button) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return !button.disabled && button.getAttribute('aria-disabled') !== 'true';
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* ExperienceCheck that observes popup mount point and all its
|
|
27
|
+
* `[data-editor-popup]` children with `{ childList: true }` (no subtree).
|
|
28
|
+
*
|
|
29
|
+
* Detects when a popup containing the given nested element is added to the
|
|
30
|
+
* DOM — either as a new `[data-editor-popup]` direct child, or as content
|
|
31
|
+
* rendered inside an existing `[data-editor-popup]` wrapper.
|
|
32
|
+
*/
|
|
33
|
+
export const TYPEAHEAD_DECORATION_SELECTOR = '[data-type-ahead="typeaheadDecoration"]';
|
|
34
|
+
export const handleTypeAheadOpenDomMutation = ({
|
|
35
|
+
mutations
|
|
36
|
+
}) => {
|
|
37
|
+
for (const mutation of mutations) {
|
|
38
|
+
if (mutation.type !== 'childList') {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
for (const node of mutation.addedNodes) {
|
|
42
|
+
if (!(node instanceof HTMLElement)) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (node.matches(TYPEAHEAD_DECORATION_SELECTOR) || node.querySelector(TYPEAHEAD_DECORATION_SELECTOR)) {
|
|
46
|
+
return {
|
|
47
|
+
status: 'success'
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
};
|
|
54
|
+
export class ExperienceCheckPopupMutation {
|
|
55
|
+
constructor(nestedElementQuery, getTarget, getEditorDom) {
|
|
56
|
+
_defineProperty(this, "observers", []);
|
|
57
|
+
this.nestedElementQuery = nestedElementQuery;
|
|
58
|
+
this.getTarget = getTarget;
|
|
59
|
+
this.getEditorDom = getEditorDom;
|
|
60
|
+
}
|
|
61
|
+
start(callback) {
|
|
62
|
+
this.stop();
|
|
63
|
+
const target = this.getTarget();
|
|
64
|
+
if (!target) {
|
|
65
|
+
callback({
|
|
66
|
+
status: 'failure',
|
|
67
|
+
reason: EXPERIENCE_FAILURE_REASON.DOM_MUTATION_TARGET_NOT_FOUND
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const doc = getDocument();
|
|
72
|
+
if (!doc) {
|
|
73
|
+
callback({
|
|
74
|
+
status: 'failure',
|
|
75
|
+
reason: EXPERIENCE_FAILURE_REASON.DOM_MUTATION_TARGET_NOT_FOUND
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const query = this.nestedElementQuery;
|
|
80
|
+
const onMutation = mutations => {
|
|
81
|
+
for (const mutation of mutations) {
|
|
82
|
+
if (mutation.type !== 'childList') {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
for (const node of mutation.addedNodes) {
|
|
86
|
+
if (!(node instanceof HTMLElement)) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (popupWithNestedElement(node, query) || node.matches(query) || !!node.querySelector(query)) {
|
|
90
|
+
this.stop();
|
|
91
|
+
callback({
|
|
92
|
+
status: 'success'
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const observe = el => {
|
|
100
|
+
const observer = new MutationObserver(onMutation);
|
|
101
|
+
observer.observe(el, {
|
|
102
|
+
childList: true
|
|
103
|
+
});
|
|
104
|
+
this.observers.push(observer);
|
|
105
|
+
};
|
|
106
|
+
observe(target);
|
|
107
|
+
for (const wrapper of target.querySelectorAll('[data-editor-popup]')) {
|
|
108
|
+
observe(wrapper);
|
|
109
|
+
}
|
|
110
|
+
const portalContainer = doc.querySelector('.atlaskit-portal-container');
|
|
111
|
+
if (portalContainer instanceof HTMLElement) {
|
|
112
|
+
const observePortal = portal => {
|
|
113
|
+
observe(portal);
|
|
114
|
+
for (const child of portal.children) {
|
|
115
|
+
if (child instanceof HTMLElement) {
|
|
116
|
+
observe(child);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const containerObserver = new MutationObserver(mutations => {
|
|
121
|
+
for (const mutation of mutations) {
|
|
122
|
+
if (mutation.type !== 'childList') {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
for (const node of mutation.addedNodes) {
|
|
126
|
+
if (node instanceof HTMLElement) {
|
|
127
|
+
observePortal(node);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
onMutation(mutations);
|
|
132
|
+
});
|
|
133
|
+
containerObserver.observe(portalContainer, {
|
|
134
|
+
childList: true
|
|
135
|
+
});
|
|
136
|
+
this.observers.push(containerObserver);
|
|
137
|
+
for (const portal of portalContainer.querySelectorAll('.atlaskit-portal')) {
|
|
138
|
+
observePortal(portal);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const editorDom = this.getEditorDom();
|
|
142
|
+
if (editorDom !== null && editorDom !== void 0 && editorDom.parentElement) {
|
|
143
|
+
observe(editorDom.parentElement);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Two-frame DOM check to handle cases where rendering happens before
|
|
147
|
+
// observers are attached.
|
|
148
|
+
const checkDom = () => {
|
|
149
|
+
if (doc.querySelector(query)) {
|
|
150
|
+
this.stop();
|
|
151
|
+
callback({
|
|
152
|
+
status: 'success'
|
|
153
|
+
});
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
requestAnimationFrame(() => {
|
|
157
|
+
if (doc.querySelector(query)) {
|
|
158
|
+
this.stop();
|
|
159
|
+
callback({
|
|
160
|
+
status: 'success'
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
requestAnimationFrame(checkDom);
|
|
166
|
+
}
|
|
167
|
+
stop() {
|
|
168
|
+
for (const observer of this.observers) {
|
|
169
|
+
observer.disconnect();
|
|
170
|
+
}
|
|
171
|
+
this.observers = [];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Returns the narrow parent DOM element at the current selection, suitable
|
|
177
|
+
* for observing with `{ childList: true }` (no subtree).
|
|
178
|
+
*
|
|
179
|
+
* Uses the resolved position's depth to find the block node at the cursor
|
|
180
|
+
* via `nodeDOM`, then returns its `parentElement` — the container whose
|
|
181
|
+
* direct children change when content is inserted at this position.
|
|
182
|
+
*
|
|
183
|
+
* Falls back to `domAtPos` if `nodeDOM` is unavailable.
|
|
184
|
+
*/
|
|
185
|
+
export const getParentDOMAtSelection = editorView => {
|
|
186
|
+
if (!editorView) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
const {
|
|
191
|
+
selection
|
|
192
|
+
} = editorView.state;
|
|
193
|
+
const $from = selection.$from;
|
|
194
|
+
const parentDepth = Math.max(1, $from.depth);
|
|
195
|
+
const parentPos = $from.before(parentDepth);
|
|
196
|
+
const parentDom = editorView.nodeDOM(parentPos);
|
|
197
|
+
if (parentDom instanceof HTMLElement && parentDom.parentElement) {
|
|
198
|
+
return parentDom.parentElement;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Fallback: use domAtPos
|
|
202
|
+
const {
|
|
203
|
+
node
|
|
204
|
+
} = editorView.domAtPos(selection.from);
|
|
205
|
+
let element = null;
|
|
206
|
+
if (node instanceof HTMLElement) {
|
|
207
|
+
element = node;
|
|
208
|
+
} else if (node instanceof Text) {
|
|
209
|
+
element = node.parentElement;
|
|
210
|
+
}
|
|
211
|
+
if (!element) {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
const proseMirrorRoot = editorView.dom;
|
|
215
|
+
if (!(proseMirrorRoot instanceof HTMLElement)) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
if (element === proseMirrorRoot) {
|
|
219
|
+
return proseMirrorRoot;
|
|
220
|
+
}
|
|
221
|
+
if (element.parentElement && proseMirrorRoot.contains(element.parentElement)) {
|
|
222
|
+
return element.parentElement;
|
|
223
|
+
}
|
|
224
|
+
return proseMirrorRoot;
|
|
225
|
+
} catch {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Checks whether a DOM node matches any known node insert marker,
|
|
232
|
+
* either directly or via a nested element (e.g. breakout mark wrapper).
|
|
233
|
+
*/
|
|
234
|
+
const matchesNodeInsertMarker = node => {
|
|
235
|
+
if (!(node instanceof HTMLElement)) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
return node.matches(COMBINED_NODE_INSERT_SELECTOR) || !!node.querySelector(COMBINED_NODE_INSERT_SELECTOR);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Evaluates DOM mutations to detect a node insert action.
|
|
243
|
+
*
|
|
244
|
+
* Uses two strategies:
|
|
245
|
+
* 1. Marker-based: checks `addedNodes` against known node insert selectors.
|
|
246
|
+
* 2. Structure-based: detects element add+remove (block-level replacement).
|
|
247
|
+
*/
|
|
248
|
+
export const handleEditorNodeInsertDomMutation = ({
|
|
249
|
+
mutations
|
|
250
|
+
}) => {
|
|
251
|
+
let hasAddedElement = false;
|
|
252
|
+
let hasRemovedElement = false;
|
|
253
|
+
for (const mutation of mutations) {
|
|
254
|
+
if (mutation.type !== 'childList') {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
for (const node of mutation.addedNodes) {
|
|
258
|
+
if (matchesNodeInsertMarker(node)) {
|
|
259
|
+
return {
|
|
260
|
+
status: 'success'
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (node instanceof HTMLElement) {
|
|
264
|
+
hasAddedElement = true;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
for (const node of mutation.removedNodes) {
|
|
268
|
+
if (node instanceof HTMLElement) {
|
|
269
|
+
hasRemovedElement = true;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (hasAddedElement && hasRemovedElement) {
|
|
274
|
+
return {
|
|
275
|
+
status: 'success'
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
return undefined;
|
|
279
|
+
};
|
|
@@ -3,6 +3,7 @@ import { useIntl } from 'react-intl-next';
|
|
|
3
3
|
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
4
4
|
import { ToolTipContent, insertEmoji } from '@atlaskit/editor-common/keymaps';
|
|
5
5
|
import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
|
|
6
|
+
import { TOOLBAR_BUTTON_TEST_ID } from '@atlaskit/editor-common/toolbar';
|
|
6
7
|
import { ToolbarButton, ToolbarTooltip, EmojiIcon, useToolbarUI } from '@atlaskit/editor-toolbar';
|
|
7
8
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
8
9
|
import { useEmojiPickerPopup } from './hooks/useEmojiPickerPopup';
|
|
@@ -69,6 +70,7 @@ export const EmojiButton = ({
|
|
|
69
70
|
ref: emojiButtonRef,
|
|
70
71
|
onClick: () => emojiPickerPopup.toggle(),
|
|
71
72
|
isSelected: emojiPickerPopup.isOpen,
|
|
72
|
-
isDisabled: !isTypeAheadAllowed || !emojiProvider
|
|
73
|
+
isDisabled: !isTypeAheadAllowed || !emojiProvider,
|
|
74
|
+
testId: TOOLBAR_BUTTON_TEST_ID.EMOJI
|
|
73
75
|
})));
|
|
74
76
|
};
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { useIntl } from 'react-intl-next';
|
|
3
3
|
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
4
4
|
import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
|
|
5
|
-
import { useEditorToolbar } from '@atlaskit/editor-common/toolbar';
|
|
5
|
+
import { useEditorToolbar, TOOLBAR_BUTTON_TEST_ID } from '@atlaskit/editor-common/toolbar';
|
|
6
6
|
import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
|
|
7
7
|
import { ToolbarButton, ToolbarTooltip, ImageIcon } from '@atlaskit/editor-toolbar';
|
|
8
8
|
export const ImageButton = ({
|
|
@@ -43,6 +43,7 @@ export const ImageButton = ({
|
|
|
43
43
|
size: "small"
|
|
44
44
|
}),
|
|
45
45
|
onClick: onClick,
|
|
46
|
-
isDisabled: !imageUploadEnabled || isOffline
|
|
46
|
+
isDisabled: !imageUploadEnabled || isOffline,
|
|
47
|
+
testId: TOOLBAR_BUTTON_TEST_ID.IMAGE
|
|
47
48
|
}));
|
|
48
49
|
};
|
|
@@ -3,7 +3,7 @@ import { useIntl } from 'react-intl-next';
|
|
|
3
3
|
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
4
4
|
import { getAriaKeyshortcuts, insertElements, ToolTipContent } from '@atlaskit/editor-common/keymaps';
|
|
5
5
|
import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
|
|
6
|
-
import { useEditorToolbar } from '@atlaskit/editor-common/toolbar';
|
|
6
|
+
import { TOOLBAR_BUTTON_TEST_ID, useEditorToolbar } from '@atlaskit/editor-common/toolbar';
|
|
7
7
|
import { Popup } from '@atlaskit/editor-common/ui';
|
|
8
8
|
import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
|
|
9
9
|
import { akEditorMenuZIndex } from '@atlaskit/editor-shared-styles';
|
|
@@ -242,6 +242,7 @@ export const InsertButton = ({
|
|
|
242
242
|
ref: insertButtonRef,
|
|
243
243
|
onClick: onClick,
|
|
244
244
|
isSelected: insertMenuOpen,
|
|
245
|
-
isDisabled: !isTypeAheadAllowed || isDisabled
|
|
245
|
+
isDisabled: !isTypeAheadAllowed || isDisabled,
|
|
246
|
+
testId: TOOLBAR_BUTTON_TEST_ID.INSERT
|
|
246
247
|
})));
|
|
247
248
|
};
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { useIntl } from 'react-intl-next';
|
|
3
3
|
import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
4
4
|
import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
|
|
5
|
-
import { useEditorToolbar } from '@atlaskit/editor-common/toolbar';
|
|
5
|
+
import { useEditorToolbar, TOOLBAR_BUTTON_TEST_ID } from '@atlaskit/editor-common/toolbar';
|
|
6
6
|
import { ToolbarButton, ToolbarTooltip, LayoutIcon } from '@atlaskit/editor-toolbar';
|
|
7
7
|
export const LayoutButton = ({
|
|
8
8
|
api
|
|
@@ -33,6 +33,7 @@ export const LayoutButton = ({
|
|
|
33
33
|
label: formatMessage(messages.columns),
|
|
34
34
|
size: "small"
|
|
35
35
|
}),
|
|
36
|
-
onClick: onClick
|
|
36
|
+
onClick: onClick,
|
|
37
|
+
testId: TOOLBAR_BUTTON_TEST_ID.LAYOUT
|
|
37
38
|
}));
|
|
38
39
|
};
|
|
@@ -3,6 +3,7 @@ import { useIntl } from 'react-intl-next';
|
|
|
3
3
|
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
4
4
|
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
5
5
|
import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
|
|
6
|
+
import { TOOLBAR_BUTTON_TEST_ID } from '@atlaskit/editor-common/toolbar';
|
|
6
7
|
import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
|
|
7
8
|
import { ToolbarButton, ToolbarTooltip, ImageIcon } from '@atlaskit/editor-toolbar';
|
|
8
9
|
export const MediaButton = ({
|
|
@@ -62,6 +63,7 @@ export const MediaButton = ({
|
|
|
62
63
|
}),
|
|
63
64
|
onClick: onClick,
|
|
64
65
|
ref: mediaButtonRef,
|
|
65
|
-
isDisabled: isOffline || !allowsUploads
|
|
66
|
+
isDisabled: isOffline || !allowsUploads,
|
|
67
|
+
testId: TOOLBAR_BUTTON_TEST_ID.MEDIA
|
|
66
68
|
}));
|
|
67
69
|
};
|
|
@@ -4,6 +4,7 @@ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
|
4
4
|
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
5
5
|
import { ToolTipContent, insertMention } from '@atlaskit/editor-common/keymaps';
|
|
6
6
|
import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
|
|
7
|
+
import { TOOLBAR_BUTTON_TEST_ID } from '@atlaskit/editor-common/toolbar';
|
|
7
8
|
import { ToolbarButton, ToolbarTooltip, MentionIcon } from '@atlaskit/editor-toolbar';
|
|
8
9
|
export const MentionButton = ({
|
|
9
10
|
api
|
|
@@ -42,6 +43,7 @@ export const MentionButton = ({
|
|
|
42
43
|
}),
|
|
43
44
|
onClick: onClick,
|
|
44
45
|
ariaKeyshortcuts: "Shift+2 Space",
|
|
45
|
-
isDisabled: !canInsertMention || !mentionProvider || !isTypeAheadAllowed
|
|
46
|
+
isDisabled: !canInsertMention || !mentionProvider || !isTypeAheadAllowed,
|
|
47
|
+
testId: TOOLBAR_BUTTON_TEST_ID.MENTION
|
|
46
48
|
}));
|
|
47
49
|
};
|