@atlaskit/editor-plugin-table 10.8.0 → 10.8.1

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @atlaskit/editor-plugin-table
2
2
 
3
+ ## 10.8.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#140847](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/140847)
8
+ [`edf99b41dbfcb`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/edf99b41dbfcb) -
9
+ ED-27529: The fix introduced to resolve tables flashing unstyled on init when R18 concurrent mode
10
+ was enabled resulted in sticky headers being broken for tables. In short, the logic for sticky
11
+ headers was not set up to handle initial prosemirror table structure being replaced with the
12
+ rendered react table node. This meant the sentinels for sticky headers were only setup on the
13
+ initial prosemirror table structure, which is removed once react is ready.
14
+
3
15
  ## 10.8.0
4
16
 
5
17
  ### Minor Changes
@@ -31,7 +31,7 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
31
31
  // limit scroll event calls
32
32
  var HEADER_ROW_SCROLL_THROTTLE_TIMEOUT = 200;
33
33
 
34
- // timeout for resetting the scroll class - if its too long then users wont be able to click on the header cells,
34
+ // timeout for resetting the scroll class - if it's too long then users won't be able to click on the header cells,
35
35
  // if too short it would trigger too many dom updates.
36
36
  var HEADER_ROW_SCROLL_RESET_DEBOUNCE_TIMEOUT = 400;
37
37
  var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
@@ -144,6 +144,9 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
144
144
  }
145
145
  this.emitOff(true);
146
146
  }
147
+ if (this.tableContainerObserver) {
148
+ this.tableContainerObserver.disconnect();
149
+ }
147
150
  }
148
151
  }, {
149
152
  key: "ignoreMutation",
@@ -252,26 +255,29 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
252
255
  this.resizeObserver.observe(this.editorScrollableElement);
253
256
  }
254
257
  window.requestAnimationFrame(function () {
255
- var _getTree;
258
+ var getTableContainer = function getTableContainer() {
259
+ var _getTree;
260
+ return (_getTree = (0, _dom2.getTree)(_this2.dom)) === null || _getTree === void 0 ? void 0 : _getTree.wrapper.closest(".".concat(_types.TableCssClassName.NODEVIEW_WRAPPER));
261
+ };
262
+
256
263
  // we expect tree to be defined after animation frame
257
- var tableContainer = (_getTree = (0, _dom2.getTree)(_this2.dom)) === null || _getTree === void 0 ? void 0 : _getTree.wrapper.closest(".".concat(_types.TableCssClassName.NODEVIEW_WRAPPER));
264
+ var tableContainer = getTableContainer();
258
265
  if (tableContainer) {
259
266
  var getSentinelTop = function getSentinelTop() {
260
- return (
261
- // Ignored via go/ees005
262
- // eslint-disable-next-line @atlaskit/editor/no-as-casting
263
- tableContainer.getElementsByClassName(_types.TableCssClassName.TABLE_STICKY_SENTINEL_TOP).item(0)
264
- );
267
+ return tableContainer &&
268
+ // Ignored via go/ees005
269
+ // eslint-disable-next-line @atlaskit/editor/no-as-casting
270
+ tableContainer.getElementsByClassName(_types.TableCssClassName.TABLE_STICKY_SENTINEL_TOP).item(0);
265
271
  };
266
272
  var getSentinelBottom = function getSentinelBottom() {
267
273
  // Multiple bottom sentinels may be found if there are nested tables.
268
274
  // We need to make sure we get the last one which will belong to the parent table.
269
- var bottomSentinels = tableContainer.getElementsByClassName(_types.TableCssClassName.TABLE_STICKY_SENTINEL_BOTTOM);
275
+ var bottomSentinels = tableContainer && tableContainer.getElementsByClassName(_types.TableCssClassName.TABLE_STICKY_SENTINEL_BOTTOM);
270
276
  // Ignored via go/ees005
271
277
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
272
278
  return (0, _platformFeatureFlags.fg)('platform_editor_nested_tables_bottom_sentinel') ?
273
279
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
274
- bottomSentinels.item(bottomSentinels.length - 1) :
280
+ bottomSentinels && bottomSentinels.item(bottomSentinels.length - 1) : tableContainer &&
275
281
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
276
282
  tableContainer.getElementsByClassName(_types.TableCssClassName.TABLE_STICKY_SENTINEL_BOTTOM).item(0);
277
283
  };
@@ -291,26 +297,60 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
291
297
  }
292
298
  });
293
299
  };
294
- if (sentinelsInDom()) {
295
- // great - DOM ready, observe as normal
296
- observeStickySentinels();
297
- } else {
298
- // concurrent loading issue - here TableRow is too eager trying to
299
- // observe sentinels before they are in the DOM, use MutationObserver
300
- // to wait for sentinels to be added to the parent Table node DOM
301
- // then attach the IntersectionObserver
302
- var tableContainerObserver = new MutationObserver(function () {
303
- if (sentinelsInDom()) {
304
- observeStickySentinels();
305
- tableContainerObserver.disconnect();
300
+ if ((0, _platformFeatureFlags.fg)('platform_editor_table_initial_load_fix')) {
301
+ var isInitialProsemirrorToDomRender = tableContainer.hasAttribute('data-prosemirror-initial-toDOM-render');
302
+
303
+ // Sentinels may be in the DOM but they're part of the prosemirror placeholder structure which is replaced with the fully rendered React node.
304
+ if (sentinelsInDom() && !isInitialProsemirrorToDomRender) {
305
+ // great - DOM ready, observe as normal
306
+ observeStickySentinels();
307
+ } else {
308
+ // concurrent loading issue - here TableRow is too eager trying to
309
+ // observe sentinels before they are in the DOM, use MutationObserver
310
+ // to wait for sentinels to be added to the parent Table node DOM
311
+ // then attach the IntersectionObserver
312
+ _this2.tableContainerObserver = new MutationObserver(function () {
313
+ // Check if the tableContainer is still connected to the DOM. It can become disconnected when the placholder
314
+ // prosemirror node is replaced with the fully rendered React node (see _handleTableRef).
315
+ if (!tableContainer || !tableContainer.isConnected) {
316
+ tableContainer = getTableContainer();
317
+ }
318
+ if (sentinelsInDom()) {
319
+ var _this2$tableContainer;
320
+ observeStickySentinels();
321
+ (_this2$tableContainer = _this2.tableContainerObserver) === null || _this2$tableContainer === void 0 || _this2$tableContainer.disconnect();
322
+ }
323
+ });
324
+ var mutatingNode = tableContainer;
325
+ if (mutatingNode && _this2.tableContainerObserver) {
326
+ _this2.tableContainerObserver.observe(mutatingNode, {
327
+ subtree: true,
328
+ childList: true
329
+ });
306
330
  }
307
- });
308
- var mutatingNode = tableContainer;
309
- if (mutatingNode) {
310
- tableContainerObserver.observe(mutatingNode, {
311
- subtree: true,
312
- childList: true
331
+ }
332
+ } else {
333
+ if (sentinelsInDom()) {
334
+ // great - DOM ready, observe as normal
335
+ observeStickySentinels();
336
+ } else {
337
+ // concurrent loading issue - here TableRow is too eager trying to
338
+ // observe sentinels before they are in the DOM, use MutationObserver
339
+ // to wait for sentinels to be added to the parent Table node DOM
340
+ // then attach the IntersectionObserver
341
+ var tableContainerObserver = new MutationObserver(function () {
342
+ if (sentinelsInDom()) {
343
+ observeStickySentinels();
344
+ tableContainerObserver.disconnect();
345
+ }
313
346
  });
347
+ var _mutatingNode = tableContainer;
348
+ if (_mutatingNode) {
349
+ tableContainerObserver.observe(_mutatingNode, {
350
+ subtree: true,
351
+ childList: true
352
+ });
353
+ }
314
354
  }
315
355
  }
316
356
  }
@@ -68,7 +68,7 @@ var handleInlineTableWidth = function handleInlineTableWidth(table, width) {
68
68
  table.style.setProperty('width', "".concat(width, "px"));
69
69
  };
70
70
 
71
- // Leave as a fallback incase the table's NodeSpec.toDOM is not defined.
71
+ // Remove after removing the platform_editor_table_initial_load_fix flag.
72
72
  var toDOM = function toDOM(node, props) {
73
73
  var colgroup = '';
74
74
  if (props.allowColumnResizing) {
@@ -68,7 +68,8 @@ var tableNodeSpecWithFixedToDOM = exports.tableNodeSpecWithFixedToDOM = function
68
68
  }]];
69
69
  if (!config.tableResizingEnabled) {
70
70
  return ['div', {
71
- class: 'tableView-content-wrap'
71
+ class: 'tableView-content-wrap',
72
+ 'data-prosemirror-initial-toDOM-render': 'true'
72
73
  }, tableContainerDiv];
73
74
  }
74
75
  var tableWidthAttribute = node.attrs.width ? "".concat(node.attrs.width, "px") : "100%";
@@ -95,7 +96,8 @@ var tableNodeSpecWithFixedToDOM = exports.tableNodeSpecWithFixedToDOM = function
95
96
  class: 'resizer-hover-zone'
96
97
  }, tableContainerDiv]]]];
97
98
  return ['div', {
98
- class: 'tableView-content-wrap'
99
+ class: 'tableView-content-wrap',
100
+ 'data-prosemirror-initial-toDOM-render': 'true'
99
101
  }, tableResizingDiv];
100
102
  }
101
103
  });
@@ -17,7 +17,7 @@ import TableNodeView from './TableNodeViewBase';
17
17
  // limit scroll event calls
18
18
  const HEADER_ROW_SCROLL_THROTTLE_TIMEOUT = 200;
19
19
 
20
- // timeout for resetting the scroll class - if its too long then users wont be able to click on the header cells,
20
+ // timeout for resetting the scroll class - if it's too long then users won't be able to click on the header cells,
21
21
  // if too short it would trigger too many dom updates.
22
22
  const HEADER_ROW_SCROLL_RESET_DEBOUNCE_TIMEOUT = 400;
23
23
  export default class TableRow extends TableNodeView {
@@ -123,6 +123,9 @@ export default class TableRow extends TableNodeView {
123
123
  }
124
124
  this.emitOff(true);
125
125
  }
126
+ if (this.tableContainerObserver) {
127
+ this.tableContainerObserver.disconnect();
128
+ }
126
129
  }
127
130
  ignoreMutation(mutationRecord) {
128
131
  /* tableRows are not directly editable by the user
@@ -222,23 +225,27 @@ export default class TableRow extends TableNodeView {
222
225
  this.resizeObserver.observe(this.editorScrollableElement);
223
226
  }
224
227
  window.requestAnimationFrame(() => {
225
- var _getTree;
228
+ const getTableContainer = () => {
229
+ var _getTree;
230
+ return (_getTree = getTree(this.dom)) === null || _getTree === void 0 ? void 0 : _getTree.wrapper.closest(`.${TableCssClassName.NODEVIEW_WRAPPER}`);
231
+ };
232
+
226
233
  // we expect tree to be defined after animation frame
227
- const tableContainer = (_getTree = getTree(this.dom)) === null || _getTree === void 0 ? void 0 : _getTree.wrapper.closest(`.${TableCssClassName.NODEVIEW_WRAPPER}`);
234
+ let tableContainer = getTableContainer();
228
235
  if (tableContainer) {
229
- const getSentinelTop = () =>
236
+ const getSentinelTop = () => tableContainer &&
230
237
  // Ignored via go/ees005
231
238
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
232
239
  tableContainer.getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_TOP).item(0);
233
240
  const getSentinelBottom = () => {
234
241
  // Multiple bottom sentinels may be found if there are nested tables.
235
242
  // We need to make sure we get the last one which will belong to the parent table.
236
- const bottomSentinels = tableContainer.getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_BOTTOM);
243
+ const bottomSentinels = tableContainer && tableContainer.getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_BOTTOM);
237
244
  // Ignored via go/ees005
238
245
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
239
246
  return fg('platform_editor_nested_tables_bottom_sentinel') ?
240
247
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
241
- bottomSentinels.item(bottomSentinels.length - 1) :
248
+ bottomSentinels && bottomSentinels.item(bottomSentinels.length - 1) : tableContainer &&
242
249
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
243
250
  tableContainer.getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_BOTTOM).item(0);
244
251
  };
@@ -256,26 +263,60 @@ export default class TableRow extends TableNodeView {
256
263
  }
257
264
  });
258
265
  };
259
- if (sentinelsInDom()) {
260
- // great - DOM ready, observe as normal
261
- observeStickySentinels();
262
- } else {
263
- // concurrent loading issue - here TableRow is too eager trying to
264
- // observe sentinels before they are in the DOM, use MutationObserver
265
- // to wait for sentinels to be added to the parent Table node DOM
266
- // then attach the IntersectionObserver
267
- const tableContainerObserver = new MutationObserver(() => {
268
- if (sentinelsInDom()) {
269
- observeStickySentinels();
270
- tableContainerObserver.disconnect();
266
+ if (fg('platform_editor_table_initial_load_fix')) {
267
+ const isInitialProsemirrorToDomRender = tableContainer.hasAttribute('data-prosemirror-initial-toDOM-render');
268
+
269
+ // Sentinels may be in the DOM but they're part of the prosemirror placeholder structure which is replaced with the fully rendered React node.
270
+ if (sentinelsInDom() && !isInitialProsemirrorToDomRender) {
271
+ // great - DOM ready, observe as normal
272
+ observeStickySentinels();
273
+ } else {
274
+ // concurrent loading issue - here TableRow is too eager trying to
275
+ // observe sentinels before they are in the DOM, use MutationObserver
276
+ // to wait for sentinels to be added to the parent Table node DOM
277
+ // then attach the IntersectionObserver
278
+ this.tableContainerObserver = new MutationObserver(() => {
279
+ // Check if the tableContainer is still connected to the DOM. It can become disconnected when the placholder
280
+ // prosemirror node is replaced with the fully rendered React node (see _handleTableRef).
281
+ if (!tableContainer || !tableContainer.isConnected) {
282
+ tableContainer = getTableContainer();
283
+ }
284
+ if (sentinelsInDom()) {
285
+ var _this$tableContainerO;
286
+ observeStickySentinels();
287
+ (_this$tableContainerO = this.tableContainerObserver) === null || _this$tableContainerO === void 0 ? void 0 : _this$tableContainerO.disconnect();
288
+ }
289
+ });
290
+ const mutatingNode = tableContainer;
291
+ if (mutatingNode && this.tableContainerObserver) {
292
+ this.tableContainerObserver.observe(mutatingNode, {
293
+ subtree: true,
294
+ childList: true
295
+ });
271
296
  }
272
- });
273
- const mutatingNode = tableContainer;
274
- if (mutatingNode) {
275
- tableContainerObserver.observe(mutatingNode, {
276
- subtree: true,
277
- childList: true
297
+ }
298
+ } else {
299
+ if (sentinelsInDom()) {
300
+ // great - DOM ready, observe as normal
301
+ observeStickySentinels();
302
+ } else {
303
+ // concurrent loading issue - here TableRow is too eager trying to
304
+ // observe sentinels before they are in the DOM, use MutationObserver
305
+ // to wait for sentinels to be added to the parent Table node DOM
306
+ // then attach the IntersectionObserver
307
+ const tableContainerObserver = new MutationObserver(() => {
308
+ if (sentinelsInDom()) {
309
+ observeStickySentinels();
310
+ tableContainerObserver.disconnect();
311
+ }
278
312
  });
313
+ const mutatingNode = tableContainer;
314
+ if (mutatingNode) {
315
+ tableContainerObserver.observe(mutatingNode, {
316
+ subtree: true,
317
+ childList: true
318
+ });
319
+ }
279
320
  }
280
321
  }
281
322
  }
@@ -50,7 +50,7 @@ const handleInlineTableWidth = (table, width) => {
50
50
  table.style.setProperty('width', `${width}px`);
51
51
  };
52
52
 
53
- // Leave as a fallback incase the table's NodeSpec.toDOM is not defined.
53
+ // Remove after removing the platform_editor_table_initial_load_fix flag.
54
54
  const toDOM = (node, props) => {
55
55
  let colgroup = '';
56
56
  if (props.allowColumnResizing) {
@@ -52,7 +52,8 @@ export const tableNodeSpecWithFixedToDOM = config => {
52
52
  }]];
53
53
  if (!config.tableResizingEnabled) {
54
54
  return ['div', {
55
- class: 'tableView-content-wrap'
55
+ class: 'tableView-content-wrap',
56
+ 'data-prosemirror-initial-toDOM-render': 'true'
56
57
  }, tableContainerDiv];
57
58
  }
58
59
  const tableWidthAttribute = node.attrs.width ? `${node.attrs.width}px` : `100%`;
@@ -79,7 +80,8 @@ export const tableNodeSpecWithFixedToDOM = config => {
79
80
  class: 'resizer-hover-zone'
80
81
  }, tableContainerDiv]]]];
81
82
  return ['div', {
82
- class: 'tableView-content-wrap'
83
+ class: 'tableView-content-wrap',
84
+ 'data-prosemirror-initial-toDOM-render': 'true'
83
85
  }, tableResizingDiv];
84
86
  }
85
87
  };
@@ -24,7 +24,7 @@ import TableNodeView from './TableNodeViewBase';
24
24
  // limit scroll event calls
25
25
  var HEADER_ROW_SCROLL_THROTTLE_TIMEOUT = 200;
26
26
 
27
- // timeout for resetting the scroll class - if its too long then users wont be able to click on the header cells,
27
+ // timeout for resetting the scroll class - if it's too long then users won't be able to click on the header cells,
28
28
  // if too short it would trigger too many dom updates.
29
29
  var HEADER_ROW_SCROLL_RESET_DEBOUNCE_TIMEOUT = 400;
30
30
  var TableRow = /*#__PURE__*/function (_TableNodeView) {
@@ -137,6 +137,9 @@ var TableRow = /*#__PURE__*/function (_TableNodeView) {
137
137
  }
138
138
  this.emitOff(true);
139
139
  }
140
+ if (this.tableContainerObserver) {
141
+ this.tableContainerObserver.disconnect();
142
+ }
140
143
  }
141
144
  }, {
142
145
  key: "ignoreMutation",
@@ -245,26 +248,29 @@ var TableRow = /*#__PURE__*/function (_TableNodeView) {
245
248
  this.resizeObserver.observe(this.editorScrollableElement);
246
249
  }
247
250
  window.requestAnimationFrame(function () {
248
- var _getTree;
251
+ var getTableContainer = function getTableContainer() {
252
+ var _getTree;
253
+ return (_getTree = getTree(_this2.dom)) === null || _getTree === void 0 ? void 0 : _getTree.wrapper.closest(".".concat(TableCssClassName.NODEVIEW_WRAPPER));
254
+ };
255
+
249
256
  // we expect tree to be defined after animation frame
250
- var tableContainer = (_getTree = getTree(_this2.dom)) === null || _getTree === void 0 ? void 0 : _getTree.wrapper.closest(".".concat(TableCssClassName.NODEVIEW_WRAPPER));
257
+ var tableContainer = getTableContainer();
251
258
  if (tableContainer) {
252
259
  var getSentinelTop = function getSentinelTop() {
253
- return (
254
- // Ignored via go/ees005
255
- // eslint-disable-next-line @atlaskit/editor/no-as-casting
256
- tableContainer.getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_TOP).item(0)
257
- );
260
+ return tableContainer &&
261
+ // Ignored via go/ees005
262
+ // eslint-disable-next-line @atlaskit/editor/no-as-casting
263
+ tableContainer.getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_TOP).item(0);
258
264
  };
259
265
  var getSentinelBottom = function getSentinelBottom() {
260
266
  // Multiple bottom sentinels may be found if there are nested tables.
261
267
  // We need to make sure we get the last one which will belong to the parent table.
262
- var bottomSentinels = tableContainer.getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_BOTTOM);
268
+ var bottomSentinels = tableContainer && tableContainer.getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_BOTTOM);
263
269
  // Ignored via go/ees005
264
270
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
265
271
  return fg('platform_editor_nested_tables_bottom_sentinel') ?
266
272
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
267
- bottomSentinels.item(bottomSentinels.length - 1) :
273
+ bottomSentinels && bottomSentinels.item(bottomSentinels.length - 1) : tableContainer &&
268
274
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
269
275
  tableContainer.getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_BOTTOM).item(0);
270
276
  };
@@ -284,26 +290,60 @@ var TableRow = /*#__PURE__*/function (_TableNodeView) {
284
290
  }
285
291
  });
286
292
  };
287
- if (sentinelsInDom()) {
288
- // great - DOM ready, observe as normal
289
- observeStickySentinels();
290
- } else {
291
- // concurrent loading issue - here TableRow is too eager trying to
292
- // observe sentinels before they are in the DOM, use MutationObserver
293
- // to wait for sentinels to be added to the parent Table node DOM
294
- // then attach the IntersectionObserver
295
- var tableContainerObserver = new MutationObserver(function () {
296
- if (sentinelsInDom()) {
297
- observeStickySentinels();
298
- tableContainerObserver.disconnect();
293
+ if (fg('platform_editor_table_initial_load_fix')) {
294
+ var isInitialProsemirrorToDomRender = tableContainer.hasAttribute('data-prosemirror-initial-toDOM-render');
295
+
296
+ // Sentinels may be in the DOM but they're part of the prosemirror placeholder structure which is replaced with the fully rendered React node.
297
+ if (sentinelsInDom() && !isInitialProsemirrorToDomRender) {
298
+ // great - DOM ready, observe as normal
299
+ observeStickySentinels();
300
+ } else {
301
+ // concurrent loading issue - here TableRow is too eager trying to
302
+ // observe sentinels before they are in the DOM, use MutationObserver
303
+ // to wait for sentinels to be added to the parent Table node DOM
304
+ // then attach the IntersectionObserver
305
+ _this2.tableContainerObserver = new MutationObserver(function () {
306
+ // Check if the tableContainer is still connected to the DOM. It can become disconnected when the placholder
307
+ // prosemirror node is replaced with the fully rendered React node (see _handleTableRef).
308
+ if (!tableContainer || !tableContainer.isConnected) {
309
+ tableContainer = getTableContainer();
310
+ }
311
+ if (sentinelsInDom()) {
312
+ var _this2$tableContainer;
313
+ observeStickySentinels();
314
+ (_this2$tableContainer = _this2.tableContainerObserver) === null || _this2$tableContainer === void 0 || _this2$tableContainer.disconnect();
315
+ }
316
+ });
317
+ var mutatingNode = tableContainer;
318
+ if (mutatingNode && _this2.tableContainerObserver) {
319
+ _this2.tableContainerObserver.observe(mutatingNode, {
320
+ subtree: true,
321
+ childList: true
322
+ });
299
323
  }
300
- });
301
- var mutatingNode = tableContainer;
302
- if (mutatingNode) {
303
- tableContainerObserver.observe(mutatingNode, {
304
- subtree: true,
305
- childList: true
324
+ }
325
+ } else {
326
+ if (sentinelsInDom()) {
327
+ // great - DOM ready, observe as normal
328
+ observeStickySentinels();
329
+ } else {
330
+ // concurrent loading issue - here TableRow is too eager trying to
331
+ // observe sentinels before they are in the DOM, use MutationObserver
332
+ // to wait for sentinels to be added to the parent Table node DOM
333
+ // then attach the IntersectionObserver
334
+ var tableContainerObserver = new MutationObserver(function () {
335
+ if (sentinelsInDom()) {
336
+ observeStickySentinels();
337
+ tableContainerObserver.disconnect();
338
+ }
306
339
  });
340
+ var _mutatingNode = tableContainer;
341
+ if (_mutatingNode) {
342
+ tableContainerObserver.observe(_mutatingNode, {
343
+ subtree: true,
344
+ childList: true
345
+ });
346
+ }
307
347
  }
308
348
  }
309
349
  }
@@ -61,7 +61,7 @@ var handleInlineTableWidth = function handleInlineTableWidth(table, width) {
61
61
  table.style.setProperty('width', "".concat(width, "px"));
62
62
  };
63
63
 
64
- // Leave as a fallback incase the table's NodeSpec.toDOM is not defined.
64
+ // Remove after removing the platform_editor_table_initial_load_fix flag.
65
65
  var toDOM = function toDOM(node, props) {
66
66
  var colgroup = '';
67
67
  if (props.allowColumnResizing) {
@@ -61,7 +61,8 @@ export var tableNodeSpecWithFixedToDOM = function tableNodeSpecWithFixedToDOM(co
61
61
  }]];
62
62
  if (!config.tableResizingEnabled) {
63
63
  return ['div', {
64
- class: 'tableView-content-wrap'
64
+ class: 'tableView-content-wrap',
65
+ 'data-prosemirror-initial-toDOM-render': 'true'
65
66
  }, tableContainerDiv];
66
67
  }
67
68
  var tableWidthAttribute = node.attrs.width ? "".concat(node.attrs.width, "px") : "100%";
@@ -88,7 +89,8 @@ export var tableNodeSpecWithFixedToDOM = function tableNodeSpecWithFixedToDOM(co
88
89
  class: 'resizer-hover-zone'
89
90
  }, tableContainerDiv]]]];
90
91
  return ['div', {
91
- class: 'tableView-content-wrap'
92
+ class: 'tableView-content-wrap',
93
+ 'data-prosemirror-initial-toDOM-render': 'true'
92
94
  }, tableResizingDiv];
93
95
  }
94
96
  });
@@ -17,6 +17,7 @@ export default class TableRow extends TableNodeView<HTMLTableRowElement> impleme
17
17
  private isSticky;
18
18
  private intersectionObserver?;
19
19
  private resizeObserver?;
20
+ private tableContainerObserver?;
20
21
  private sentinels;
21
22
  private sentinelData;
22
23
  private stickyRowHeight?;
@@ -17,6 +17,7 @@ export default class TableRow extends TableNodeView<HTMLTableRowElement> impleme
17
17
  private isSticky;
18
18
  private intersectionObserver?;
19
19
  private resizeObserver?;
20
+ private tableContainerObserver?;
20
21
  private sentinels;
21
22
  private sentinelData;
22
23
  private stickyRowHeight?;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-table",
3
- "version": "10.8.0",
3
+ "version": "10.8.1",
4
4
  "description": "Table plugin for the @atlaskit/editor",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -32,7 +32,7 @@
32
32
  "@atlaskit/adf-schema": "^47.6.0",
33
33
  "@atlaskit/button": "^23.0.0",
34
34
  "@atlaskit/custom-steps": "^0.11.0",
35
- "@atlaskit/editor-common": "^103.7.0",
35
+ "@atlaskit/editor-common": "^103.9.0",
36
36
  "@atlaskit/editor-palette": "^2.1.0",
37
37
  "@atlaskit/editor-plugin-accessibility-utils": "^2.0.0",
38
38
  "@atlaskit/editor-plugin-analytics": "^2.2.0",
@@ -54,9 +54,9 @@
54
54
  "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.0",
55
55
  "@atlaskit/primitives": "^14.4.0",
56
56
  "@atlaskit/theme": "^18.0.0",
57
- "@atlaskit/tmp-editor-statsig": "^4.8.0",
57
+ "@atlaskit/tmp-editor-statsig": "^4.11.0",
58
58
  "@atlaskit/toggle": "^15.0.0",
59
- "@atlaskit/tokens": "^4.7.0",
59
+ "@atlaskit/tokens": "^4.8.0",
60
60
  "@atlaskit/tooltip": "^20.0.0",
61
61
  "@babel/runtime": "^7.0.0",
62
62
  "@emotion/react": "^11.7.1",
@@ -39,7 +39,7 @@ interface SentinelData {
39
39
  // limit scroll event calls
40
40
  const HEADER_ROW_SCROLL_THROTTLE_TIMEOUT = 200;
41
41
 
42
- // timeout for resetting the scroll class - if its too long then users wont be able to click on the header cells,
42
+ // timeout for resetting the scroll class - if it's too long then users won't be able to click on the header cells,
43
43
  // if too short it would trigger too many dom updates.
44
44
  const HEADER_ROW_SCROLL_RESET_DEBOUNCE_TIMEOUT = 400;
45
45
 
@@ -86,6 +86,7 @@ export default class TableRow extends TableNodeView<HTMLTableRowElement> impleme
86
86
  private isSticky: boolean;
87
87
  private intersectionObserver?: IntersectionObserver;
88
88
  private resizeObserver?: ResizeObserver;
89
+ private tableContainerObserver?: MutationObserver;
89
90
  private sentinels: {
90
91
  top?: HTMLElement | null;
91
92
  bottom?: HTMLElement | null;
@@ -157,6 +158,10 @@ export default class TableRow extends TableNodeView<HTMLTableRowElement> impleme
157
158
 
158
159
  this.emitOff(true);
159
160
  }
161
+
162
+ if (this.tableContainerObserver) {
163
+ this.tableContainerObserver.disconnect();
164
+ }
160
165
  }
161
166
 
162
167
  ignoreMutation(mutationRecord: MutationRecord | { type: 'selection'; target: Node }) {
@@ -289,32 +294,36 @@ export default class TableRow extends TableNodeView<HTMLTableRowElement> impleme
289
294
  }
290
295
 
291
296
  window.requestAnimationFrame(() => {
297
+ const getTableContainer = () =>
298
+ getTree(this.dom)?.wrapper.closest(`.${TableCssClassName.NODEVIEW_WRAPPER}`);
299
+
292
300
  // we expect tree to be defined after animation frame
293
- const tableContainer = getTree(this.dom)?.wrapper.closest(
294
- `.${TableCssClassName.NODEVIEW_WRAPPER}`,
295
- );
301
+ let tableContainer = getTableContainer();
302
+
296
303
  if (tableContainer) {
297
304
  const getSentinelTop = () =>
305
+ tableContainer &&
298
306
  // Ignored via go/ees005
299
307
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
300
- tableContainer
308
+ (tableContainer
301
309
  .getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_TOP)
302
- .item(0) as HTMLElement;
310
+ .item(0) as HTMLElement);
303
311
  const getSentinelBottom = () => {
304
312
  // Multiple bottom sentinels may be found if there are nested tables.
305
313
  // We need to make sure we get the last one which will belong to the parent table.
306
- const bottomSentinels = tableContainer.getElementsByClassName(
307
- ClassName.TABLE_STICKY_SENTINEL_BOTTOM,
308
- );
314
+ const bottomSentinels =
315
+ tableContainer &&
316
+ tableContainer.getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_BOTTOM);
309
317
  // Ignored via go/ees005
310
318
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
311
319
  return fg('platform_editor_nested_tables_bottom_sentinel')
312
320
  ? // eslint-disable-next-line @atlaskit/editor/no-as-casting
313
- (bottomSentinels.item(bottomSentinels.length - 1) as HTMLElement)
314
- : // eslint-disable-next-line @atlaskit/editor/no-as-casting
315
- (tableContainer
316
- .getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_BOTTOM)
317
- .item(0) as HTMLElement);
321
+ bottomSentinels && (bottomSentinels.item(bottomSentinels.length - 1) as HTMLElement)
322
+ : tableContainer &&
323
+ // eslint-disable-next-line @atlaskit/editor/no-as-casting
324
+ (tableContainer
325
+ .getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_BOTTOM)
326
+ .item(0) as HTMLElement);
318
327
  };
319
328
 
320
329
  const sentinelsInDom = () => getSentinelTop() !== null && getSentinelBottom() !== null;
@@ -333,24 +342,57 @@ export default class TableRow extends TableNodeView<HTMLTableRowElement> impleme
333
342
  });
334
343
  };
335
344
 
336
- if (sentinelsInDom()) {
337
- // great - DOM ready, observe as normal
338
- observeStickySentinels();
345
+ if (fg('platform_editor_table_initial_load_fix')) {
346
+ const isInitialProsemirrorToDomRender = tableContainer.hasAttribute(
347
+ 'data-prosemirror-initial-toDOM-render',
348
+ );
349
+
350
+ // Sentinels may be in the DOM but they're part of the prosemirror placeholder structure which is replaced with the fully rendered React node.
351
+ if (sentinelsInDom() && !isInitialProsemirrorToDomRender) {
352
+ // great - DOM ready, observe as normal
353
+ observeStickySentinels();
354
+ } else {
355
+ // concurrent loading issue - here TableRow is too eager trying to
356
+ // observe sentinels before they are in the DOM, use MutationObserver
357
+ // to wait for sentinels to be added to the parent Table node DOM
358
+ // then attach the IntersectionObserver
359
+ this.tableContainerObserver = new MutationObserver(() => {
360
+ // Check if the tableContainer is still connected to the DOM. It can become disconnected when the placholder
361
+ // prosemirror node is replaced with the fully rendered React node (see _handleTableRef).
362
+ if (!tableContainer || !tableContainer.isConnected) {
363
+ tableContainer = getTableContainer();
364
+ }
365
+ if (sentinelsInDom()) {
366
+ observeStickySentinels();
367
+ this.tableContainerObserver?.disconnect();
368
+ }
369
+ });
370
+
371
+ const mutatingNode = tableContainer;
372
+ if (mutatingNode && this.tableContainerObserver) {
373
+ this.tableContainerObserver.observe(mutatingNode, { subtree: true, childList: true });
374
+ }
375
+ }
339
376
  } else {
340
- // concurrent loading issue - here TableRow is too eager trying to
341
- // observe sentinels before they are in the DOM, use MutationObserver
342
- // to wait for sentinels to be added to the parent Table node DOM
343
- // then attach the IntersectionObserver
344
- const tableContainerObserver = new MutationObserver(() => {
345
- if (sentinelsInDom()) {
346
- observeStickySentinels();
347
- tableContainerObserver.disconnect();
377
+ if (sentinelsInDom()) {
378
+ // great - DOM ready, observe as normal
379
+ observeStickySentinels();
380
+ } else {
381
+ // concurrent loading issue - here TableRow is too eager trying to
382
+ // observe sentinels before they are in the DOM, use MutationObserver
383
+ // to wait for sentinels to be added to the parent Table node DOM
384
+ // then attach the IntersectionObserver
385
+ const tableContainerObserver = new MutationObserver(() => {
386
+ if (sentinelsInDom()) {
387
+ observeStickySentinels();
388
+ tableContainerObserver.disconnect();
389
+ }
390
+ });
391
+
392
+ const mutatingNode = tableContainer;
393
+ if (mutatingNode) {
394
+ tableContainerObserver.observe(mutatingNode, { subtree: true, childList: true });
348
395
  }
349
- });
350
-
351
- const mutatingNode = tableContainer;
352
- if (mutatingNode) {
353
- tableContainerObserver.observe(mutatingNode, { subtree: true, childList: true });
354
396
  }
355
397
  }
356
398
  }
@@ -81,7 +81,7 @@ const handleInlineTableWidth = (table: HTMLElement, width: number | undefined) =
81
81
  table.style.setProperty('width', `${width}px`);
82
82
  };
83
83
 
84
- // Leave as a fallback incase the table's NodeSpec.toDOM is not defined.
84
+ // Remove after removing the platform_editor_table_initial_load_fix flag.
85
85
  const toDOM = (node: PmNode, props: Props) => {
86
86
  let colgroup: DOMOutputSpec = '';
87
87
  if (props.allowColumnResizing) {
@@ -111,6 +111,7 @@ export const tableNodeSpecWithFixedToDOM = (
111
111
  'div',
112
112
  {
113
113
  class: 'tableView-content-wrap',
114
+ 'data-prosemirror-initial-toDOM-render': 'true',
114
115
  },
115
116
  tableContainerDiv,
116
117
  ];
@@ -161,6 +162,7 @@ export const tableNodeSpecWithFixedToDOM = (
161
162
  'div',
162
163
  {
163
164
  class: 'tableView-content-wrap',
165
+ 'data-prosemirror-initial-toDOM-render': 'true',
164
166
  },
165
167
  tableResizingDiv,
166
168
  ];