@atlaskit/editor-plugin-table 0.2.6 → 1.0.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.
Files changed (83) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/cjs/plugins/table/commands/index.js +9 -1
  3. package/dist/cjs/plugins/table/commands/referentiality.js +23 -0
  4. package/dist/cjs/plugins/table/event-handlers.js +5 -1
  5. package/dist/cjs/plugins/table/nodeviews/table.js +10 -1
  6. package/dist/cjs/plugins/table/nodeviews/tableCell.js +5 -1
  7. package/dist/cjs/plugins/table/pm-plugins/decorations/plugin.js +1 -0
  8. package/dist/cjs/plugins/table/pm-plugins/decorations/utils/column-controls.js +1 -3
  9. package/dist/cjs/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.js +15 -12
  10. package/dist/cjs/plugins/table/toolbar.js +78 -35
  11. package/dist/cjs/plugins/table/ui/common-styles.js +6 -3
  12. package/dist/cjs/plugins/table/ui/messages.js +17 -2
  13. package/dist/cjs/plugins/table/ui/ui-styles.js +2 -7
  14. package/dist/cjs/plugins/table/utils/decoration.js +19 -12
  15. package/dist/cjs/version.json +1 -1
  16. package/dist/es2019/plugins/table/commands/index.js +2 -1
  17. package/dist/es2019/plugins/table/commands/referentiality.js +10 -0
  18. package/dist/es2019/plugins/table/event-handlers.js +3 -1
  19. package/dist/es2019/plugins/table/nodeviews/table.js +13 -1
  20. package/dist/es2019/plugins/table/nodeviews/tableCell.js +5 -1
  21. package/dist/es2019/plugins/table/pm-plugins/decorations/plugin.js +1 -0
  22. package/dist/es2019/plugins/table/pm-plugins/decorations/utils/column-controls.js +1 -2
  23. package/dist/es2019/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.js +16 -11
  24. package/dist/es2019/plugins/table/toolbar.js +65 -29
  25. package/dist/es2019/plugins/table/ui/common-styles.js +15 -5
  26. package/dist/es2019/plugins/table/ui/messages.js +17 -2
  27. package/dist/es2019/plugins/table/ui/ui-styles.js +24 -16
  28. package/dist/es2019/plugins/table/utils/decoration.js +22 -13
  29. package/dist/es2019/version.json +1 -1
  30. package/dist/esm/plugins/table/commands/index.js +2 -1
  31. package/dist/esm/plugins/table/commands/referentiality.js +12 -0
  32. package/dist/esm/plugins/table/event-handlers.js +5 -1
  33. package/dist/esm/plugins/table/nodeviews/table.js +10 -1
  34. package/dist/esm/plugins/table/nodeviews/tableCell.js +5 -1
  35. package/dist/esm/plugins/table/pm-plugins/decorations/plugin.js +1 -0
  36. package/dist/esm/plugins/table/pm-plugins/decorations/utils/column-controls.js +1 -2
  37. package/dist/esm/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.js +15 -12
  38. package/dist/esm/plugins/table/toolbar.js +74 -29
  39. package/dist/esm/plugins/table/ui/common-styles.js +6 -3
  40. package/dist/esm/plugins/table/ui/messages.js +17 -2
  41. package/dist/esm/plugins/table/ui/ui-styles.js +2 -6
  42. package/dist/esm/plugins/table/utils/decoration.js +19 -12
  43. package/dist/esm/version.json +1 -1
  44. package/dist/types/plugins/table/commands/index.d.ts +1 -0
  45. package/dist/types/plugins/table/commands/referentiality.d.ts +2 -0
  46. package/dist/types/plugins/table/index.d.ts +2 -3
  47. package/dist/types/plugins/table/nodeviews/table.d.ts +4 -1
  48. package/dist/types/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.d.ts +0 -1
  49. package/dist/types/plugins/table/ui/common-styles.d.ts +4 -1
  50. package/dist/types/plugins/table/ui/messages.d.ts +15 -0
  51. package/package.json +6 -6
  52. package/report.api.md +6 -6
  53. package/src/__tests__/integration/__fixtures__/table-and-paragraph-adf.json +130 -0
  54. package/src/__tests__/integration/floating-toolbar.ts +54 -0
  55. package/src/__tests__/integration/meta-arrowup-cursor-in-first-row.ts +37 -0
  56. package/src/__tests__/unit/color-picker.ts +100 -0
  57. package/src/__tests__/unit/get-toolbar-config.ts +1 -4
  58. package/src/__tests__/unit/keymap.ts +1 -1
  59. package/src/__tests__/unit/nodeviews/cell.ts +52 -57
  60. package/src/__tests__/unit/pm-plugins/sticky-headers/tableRow.tsx +90 -8
  61. package/src/__tests__/unit/transforms/delete-columns.ts +1 -1
  62. package/src/__tests__/unit/transforms/delete-rows.ts +1 -1
  63. package/src/__tests__/unit/utils/collapse.ts +2 -2
  64. package/src/__tests__/visual-regression/__image_snapshots__/cell-options-menu-ts-table-cell-options-menu-delete-row-menu-item-should-remove-the-table-row-on-click-1-snap.png +2 -2
  65. package/src/plugins/table/commands/index.ts +1 -0
  66. package/src/plugins/table/commands/referentiality.ts +14 -0
  67. package/src/plugins/table/event-handlers.ts +7 -1
  68. package/src/plugins/table/index.tsx +6 -1
  69. package/src/plugins/table/nodeviews/table.tsx +16 -1
  70. package/src/plugins/table/nodeviews/tableCell.tsx +3 -1
  71. package/src/plugins/table/pm-plugins/decorations/plugin.ts +1 -0
  72. package/src/plugins/table/pm-plugins/decorations/utils/column-controls.ts +1 -1
  73. package/src/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.ts +12 -10
  74. package/src/plugins/table/toolbar.tsx +84 -28
  75. package/src/plugins/table/ui/common-styles.ts +20 -4
  76. package/src/plugins/table/ui/messages.ts +18 -3
  77. package/src/plugins/table/ui/ui-styles.ts +23 -14
  78. package/src/plugins/table/utils/decoration.ts +36 -20
  79. package/dist/cjs/plugins/table/utils/referentiality.js +0 -29
  80. package/dist/es2019/plugins/table/utils/referentiality.js +0 -18
  81. package/dist/esm/plugins/table/utils/referentiality.js +0 -20
  82. package/dist/types/plugins/table/utils/referentiality.d.ts +0 -2
  83. package/src/plugins/table/utils/referentiality.ts +0 -24
@@ -54,9 +54,10 @@ describe('TableRowNodeView', () => {
54
54
  return createEditor({
55
55
  doc,
56
56
  preset: new Preset<LightEditorPlugin>()
57
- .add([tablePlugin])
57
+ .add(tablePlugin)
58
58
  .add([featureFlagsPlugin, featureFlags]),
59
59
  pluginKey,
60
+ attachTo: document.body,
60
61
  });
61
62
  };
62
63
  let editorView: EditorView;
@@ -81,7 +82,7 @@ describe('TableRowNodeView', () => {
81
82
  const editorWithTableSticky = (doc: DocBuilder) =>
82
83
  createEditor({
83
84
  doc,
84
- preset: new Preset<LightEditorPlugin>().add([tablePlugin]),
85
+ preset: new Preset<LightEditorPlugin>().add(tablePlugin),
85
86
  pluginKey,
86
87
  });
87
88
  const editorData = editorWithTableSticky(
@@ -349,44 +350,42 @@ describe('TableRowNodeView', () => {
349
350
  tableRowNodeView.dom = tableRowDom;
350
351
  });
351
352
 
353
+ afterEach(() => {
354
+ jest.clearAllMocks();
355
+ });
356
+
352
357
  function getTableElements(tableRowDom: HTMLTableRowElement) {
353
358
  const tableWrapper = tableRowDom.closest(
354
359
  `.${TableCssClassName.NODEVIEW_WRAPPER}`,
355
360
  );
356
361
  const tableElement = tableRowDom.closest('table');
357
362
  const tableParent = tableElement?.parentElement;
358
-
359
363
  const scrollContainer = tableWrapper?.parentElement;
360
364
  return { tableWrapper, tableElement, tableParent, scrollContainer };
361
365
  }
362
-
363
366
  function mockScrollPositions(tableRowDom: HTMLTableRowElement) {
364
367
  const { tableWrapper, tableParent, scrollContainer } =
365
368
  getTableElements(tableRowDom);
366
369
  (findOverflowScrollParent as unknown as jest.SpyInstance).mockReturnValue(
367
370
  scrollContainer,
368
371
  );
369
-
370
372
  jest
371
373
  .spyOn(scrollContainer as HTMLElement, 'getBoundingClientRect')
372
374
  .mockImplementationOnce(() => ({
373
375
  ...baseBoundingRect,
374
376
  top: -50,
375
377
  }));
376
-
377
378
  jest
378
379
  .spyOn(tableParent as HTMLElement, 'getBoundingClientRect')
379
380
  .mockImplementationOnce(() => ({
380
381
  ...baseBoundingRect,
381
382
  }));
382
-
383
383
  jest
384
384
  .spyOn(tableWrapper as HTMLElement, 'getBoundingClientRect')
385
385
  .mockImplementationOnce(() => ({
386
386
  ...baseBoundingRect,
387
387
  top: -100,
388
388
  }));
389
-
390
389
  return scrollContainer;
391
390
  }
392
391
 
@@ -580,4 +579,87 @@ describe('TableRowNodeView', () => {
580
579
  expect(sentinelBottom.dataset.isObserved).toBeUndefined();
581
580
  });
582
581
  });
582
+
583
+ describe('makeRowHeaderNotSticky', () => {
584
+ let makeRowHeaderNotStickySpy: jest.SpyInstance;
585
+ let tableRef: HTMLElement;
586
+ beforeEach(() => {
587
+ const editorData = editor(
588
+ doc(table()(tr(thEmpty, thEmpty), tr(tdEmpty, tdEmpty))),
589
+ true, // toggle to enable optimization
590
+ );
591
+ editorView = editorData.editorView;
592
+ eventDispatcher = editorData.eventDispatcher;
593
+ tableRowNode = editorView.state.doc.firstChild!.firstChild!;
594
+ tableRowDom = editorView.dom.getElementsByTagName('tr')[0];
595
+
596
+ tableRowNodeView = new TableRowNodeView(
597
+ tableRowNode,
598
+ editorView,
599
+ jest.fn(),
600
+ eventDispatcher,
601
+ fakeGetEditorFeatureFlags,
602
+ );
603
+ tableRowNodeView.dom = tableRowDom;
604
+
605
+ // Initialize with sticky off
606
+ tableRowNodeView.isSticky = false;
607
+ tableRowNodeView.top = 0;
608
+ tableRowNodeView.padding = 0;
609
+
610
+ makeRowHeaderNotStickySpy = jest.spyOn(
611
+ tableRowNodeView as any,
612
+ 'makeRowHeaderNotSticky',
613
+ );
614
+ tableRef = document.querySelector(
615
+ '.ProseMirror table',
616
+ ) as HTMLTableElement;
617
+ });
618
+
619
+ afterEach(() => {
620
+ jest.clearAllMocks();
621
+ });
622
+
623
+ it('should not be called if table is not selected', () => {
624
+ eventDispatcher.emit((pluginKey as any).key, {
625
+ isHeaderRowEnabled: false,
626
+ tableRef: null,
627
+ });
628
+ expect(makeRowHeaderNotStickySpy).not.toHaveBeenCalled();
629
+ });
630
+
631
+ it('should not be called if header row is enabled', () => {
632
+ eventDispatcher.emit((pluginKey as any).key, {
633
+ isHeaderRowEnabled: true,
634
+ tableRef,
635
+ });
636
+ expect(makeRowHeaderNotStickySpy).not.toHaveBeenCalled();
637
+ });
638
+
639
+ it('should be called if header row is disabled and table is selected', () => {
640
+ eventDispatcher.emit((pluginKey as any).key, {
641
+ isHeaderRowEnabled: false,
642
+ tableRef,
643
+ });
644
+ expect(makeRowHeaderNotStickySpy).toHaveBeenCalled();
645
+ });
646
+
647
+ it('should cause isSticky state to be set to false when called', () => {
648
+ // Begin test with stickyheaders state on
649
+ tableRowNodeView.isSticky = true;
650
+ tableRowNodeView.top = 1;
651
+ tableRowNodeView.padding = 1;
652
+
653
+ tableRowNodeView.makeRowHeaderNotSticky(tableRef);
654
+
655
+ expect(updateStickyState).toHaveBeenCalledWith(
656
+ expect.objectContaining({
657
+ sticky: false,
658
+ }),
659
+ );
660
+ expect(tableRowNodeView.isSticky).toBe(false);
661
+ expect(tableRowNodeView.top).toBe(0);
662
+ expect(tableRowNodeView.padding).toBe(0);
663
+ });
664
+ });
583
665
  });
@@ -41,7 +41,7 @@ describe('table plugin -> transforms -> delete columns', () => {
41
41
  });
42
42
 
43
43
  const createEditor = createProsemirrorEditorFactory();
44
- const preset = new Preset<LightEditorPlugin>().add([tablePlugin]);
44
+ const preset = new Preset<LightEditorPlugin>().add(tablePlugin);
45
45
 
46
46
  const editor = (doc: DocBuilder) =>
47
47
  createEditor<TablePluginState, PluginKey>({
@@ -41,7 +41,7 @@ describe('table plugin -> transforms -> delete rows', () => {
41
41
  uuid.setStatic(false);
42
42
  });
43
43
  const createEditor = createProsemirrorEditorFactory();
44
- const preset = new Preset<LightEditorPlugin>().add([tablePlugin]);
44
+ const preset = new Preset<LightEditorPlugin>().add(tablePlugin);
45
45
 
46
46
  const editor = (doc: DocBuilder) =>
47
47
  createEditor<TablePluginState, PluginKey>({
@@ -23,9 +23,9 @@ describe('collapse', () => {
23
23
  const createEditor = createProsemirrorEditorFactory();
24
24
 
25
25
  const editor = (doc: DocBuilder, expandInPlugins?: boolean) => {
26
- const preset = new Preset<LightEditorPlugin>().add([tablePlugin]);
26
+ const preset = new Preset<LightEditorPlugin>().add(tablePlugin);
27
27
 
28
- const finalPreset = expandInPlugins ? preset.add([expandPlugin]) : preset;
28
+ const finalPreset = expandInPlugins ? preset.add(expandPlugin) : preset;
29
29
 
30
30
  return createEditor({ doc, preset: finalPreset });
31
31
  };
@@ -1,3 +1,3 @@
1
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:5cd747accdc40bfc52f004ff689a3df00173ee2f27d15c68f3a76172c11050fd
3
- size 13990
2
+ oid sha256:185a432916fe30487eec362617c510dd132f2a37a56bfceeca674da4c6512dc9
3
+ size 13986
@@ -38,3 +38,4 @@ export {
38
38
  } from './misc';
39
39
  export { sortByColumn } from './sort';
40
40
  export { goToNextCell } from './go-to-next-cell';
41
+ export { removeDescendantNodes } from './referentiality';
@@ -0,0 +1,14 @@
1
+ import { createCommand } from '../pm-plugins/plugin-factory';
2
+ import { Node as PMNode } from 'prosemirror-model';
3
+ import { removeConnectedNodes } from '@atlaskit/editor-common/utils';
4
+
5
+ export const removeDescendantNodes = (sourceNode: PMNode) =>
6
+ createCommand(
7
+ {
8
+ type: 'UPDATE_STATE',
9
+ data: { element: undefined },
10
+ },
11
+ (tr, state) => {
12
+ return sourceNode ? removeConnectedNodes(state, sourceNode) : tr;
13
+ },
14
+ );
@@ -78,6 +78,11 @@ const isFocusingModal = (event: Event) =>
78
78
  event.relatedTarget instanceof HTMLElement &&
79
79
  event.relatedTarget.closest('[role="dialog"]');
80
80
 
81
+ const isFocusingFloatingToolbar = (event: Event) =>
82
+ event instanceof FocusEvent &&
83
+ event.relatedTarget instanceof HTMLElement &&
84
+ event.relatedTarget.closest('[role="toolbar"]');
85
+
81
86
  export const handleBlur = (view: EditorView, event: Event): boolean => {
82
87
  const { state, dispatch } = view;
83
88
  // IE version check for ED-4665
@@ -85,7 +90,8 @@ export const handleBlur = (view: EditorView, event: Event): boolean => {
85
90
  if (
86
91
  browser.ie_version !== 11 &&
87
92
  !isFocusingCalendar(event) &&
88
- !isFocusingModal(event)
93
+ !isFocusingModal(event) &&
94
+ !isFocusingFloatingToolbar(event)
89
95
  ) {
90
96
  setEditorFocus(false)(state, dispatch);
91
97
  }
@@ -59,6 +59,7 @@ import { ErrorBoundary } from '@atlaskit/editor-common/error-boundary';
59
59
  import type {
60
60
  GetEditorContainerWidth,
61
61
  GetEditorFeatureFlags,
62
+ NextEditorPlugin,
62
63
  } from '@atlaskit/editor-common/types';
63
64
  import { EditorState, Transaction } from 'prosemirror-state';
64
65
 
@@ -76,7 +77,11 @@ interface TablePluginOptions {
76
77
 
77
78
  const defaultGetEditorFeatureFlags = () => ({});
78
79
 
79
- const tablesPlugin = (options?: TablePluginOptions): EditorPlugin => {
80
+ const tablesPlugin: NextEditorPlugin<
81
+ 'table',
82
+ never,
83
+ TablePluginOptions | undefined
84
+ > = (options?: TablePluginOptions) => {
80
85
  const editorViewRef: Record<'current', EditorView | null> = { current: null };
81
86
  const defaultGetEditorContainerWidth: GetEditorContainerWidth = () => {
82
87
  if (!editorViewRef.current) {
@@ -209,7 +209,22 @@ export default class TableView extends ReactNodeView<Props> {
209
209
  return super.viewShouldUpdate(nextNode);
210
210
  }
211
211
 
212
- ignoreMutation() {
212
+ ignoreMutation(
213
+ mutation: MutationRecord | { type: 'selection'; target: Element },
214
+ ) {
215
+ const {
216
+ type,
217
+ target: { nodeName, firstChild },
218
+ } = mutation;
219
+
220
+ if (
221
+ type === 'selection' &&
222
+ nodeName?.toUpperCase() === 'DIV' &&
223
+ firstChild?.nodeName.toUpperCase() === 'TABLE'
224
+ ) {
225
+ return false;
226
+ }
227
+
213
228
  return true;
214
229
  }
215
230
 
@@ -99,7 +99,9 @@ export default class TableCellNodeView implements NodeView {
99
99
 
100
100
  update(node: Node) {
101
101
  const didUpdate = this.updateNodeView(node);
102
- this.node = node;
102
+ if (didUpdate) {
103
+ this.node = node;
104
+ }
103
105
  return didUpdate;
104
106
  }
105
107
 
@@ -60,6 +60,7 @@ export const createPlugin = () => {
60
60
  }
61
61
 
62
62
  if (tr.docChanged || tr.selectionSet) {
63
+ pluginState = pluginState.map(tr.mapping, tr.doc);
63
64
  return handleDocOrSelectionChanged(tr, pluginState, oldState);
64
65
  }
65
66
 
@@ -83,5 +83,5 @@ export const buildColumnControlsDecorations: DecorationTransformer = ({
83
83
  removeControlsHoverDecoration,
84
84
  maybeUpdateColumnSelectedDecoration,
85
85
  maybeUpdateColumnControlsDecoration,
86
- ])({ decorationSet: DecorationSet.empty, tr });
86
+ ])({ decorationSet, tr });
87
87
  };
@@ -97,7 +97,6 @@ export class TableRowNodeView implements NodeView {
97
97
  focused = false;
98
98
  topPosEditorElement = 0;
99
99
  isSticky: boolean;
100
- isTableInit: boolean;
101
100
  lastTimePainted: number;
102
101
 
103
102
  private intersectionObserver?: IntersectionObserver;
@@ -134,7 +133,6 @@ export class TableRowNodeView implements NodeView {
134
133
  this.lastTimePainted = 0;
135
134
  this.isHeaderRow = supportedHeaderRow(node);
136
135
  this.isSticky = false;
137
- this.isTableInit = false;
138
136
 
139
137
  if (this.isHeaderRow) {
140
138
  this.dom.setAttribute('data-header-row', 'true');
@@ -486,14 +484,6 @@ export class TableRowNodeView implements NodeView {
486
484
  return;
487
485
  }
488
486
 
489
- // make it non-sticky initially to avoid making it look separated
490
- if (!this.isTableInit) {
491
- if (this.tree) {
492
- this.makeRowHeaderNotSticky(this.tree.table);
493
- this.isTableInit = true;
494
- }
495
- }
496
-
497
487
  // when header rows are toggled off - mark sentinels as unobserved
498
488
  if (!state.isHeaderRowEnabled) {
499
489
  [this.sentinels.top, this.sentinels.bottom].forEach((el) => {
@@ -504,6 +494,11 @@ export class TableRowNodeView implements NodeView {
504
494
  }
505
495
 
506
496
  const isCurrentTableSelected = tableRef === tree.table;
497
+
498
+ // If current table selected and header row is toggled off, turn off sticky header
499
+ if (isCurrentTableSelected && !state.isHeaderRowEnabled && this.tree) {
500
+ this.makeRowHeaderNotSticky(this.tree.table);
501
+ }
507
502
  if (isCurrentTableSelected !== this.focused) {
508
503
  focusChanged = true;
509
504
  }
@@ -598,6 +593,13 @@ export class TableRowNodeView implements NodeView {
598
593
 
599
594
  const { table } = tree;
600
595
 
596
+ // ED-16035 Make sure sticky header is only applied to first row
597
+ const tbody = this.dom.parentElement;
598
+ const isFirstHeader = tbody?.firstChild?.isEqualNode(this.dom);
599
+ if (!isFirstHeader) {
600
+ return;
601
+ }
602
+
601
603
  const currentTableTop = this.getCurrentTableTop(tree);
602
604
  const domTop =
603
605
  currentTableTop > 0
@@ -7,6 +7,7 @@ import commonMessages from '@atlaskit/editor-common/messages';
7
7
  import type {
8
8
  Command,
9
9
  CommandDispatch,
10
+ ConfirmDialogOptions,
10
11
  DropdownOptionT,
11
12
  FloatingToolbarDropdown,
12
13
  FloatingToolbarHandler,
@@ -21,6 +22,7 @@ import {
21
22
  hoverTable,
22
23
  hoverColumns,
23
24
  hoverRows,
25
+ removeDescendantNodes,
24
26
  } from './commands';
25
27
  import {
26
28
  deleteTableWithAnalytics,
@@ -54,7 +56,12 @@ import {
54
56
  getSelectedColumnIndexes,
55
57
  getSelectedRowIndexes,
56
58
  } from './utils';
57
- import { isReferencedSource } from './utils/referentiality';
59
+ import {
60
+ isReferencedSource,
61
+ getChildrenInfo,
62
+ getNodeName,
63
+ } from '@atlaskit/editor-common/utils';
64
+
58
65
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
59
66
  import {
60
67
  findCellRectClosestToPos,
@@ -72,6 +79,7 @@ import type { GetEditorContainerWidth } from '@atlaskit/editor-common/types';
72
79
  import { Rect } from '@atlaskit/editor-tables/table-map';
73
80
  import { findParentDomRefOfType } from 'prosemirror-utils';
74
81
  import { EditorView } from 'prosemirror-view';
82
+ import { Node as PMNode } from 'prosemirror-model';
75
83
  import { closestElement } from '@atlaskit/editor-common/utils';
76
84
  import {
77
85
  addColumnAfter,
@@ -157,6 +165,7 @@ export const getToolbarMenuConfig = (
157
165
  return {
158
166
  id: 'editor.table.tableOptions',
159
167
  type: 'dropdown',
168
+ testId: 'table_options',
160
169
  title: formatMessage(messages.tableOptions),
161
170
  hidden: options.every((option) => option.hidden),
162
171
  options,
@@ -239,17 +248,9 @@ export const getToolbarCellOptionsConfig = (
239
248
  }
240
249
  return true;
241
250
  },
242
- onMouseOver: (state: EditorState, dispatch?: CommandDispatch) => {
243
- const selectionRect = getClosestSelectionRect(state);
244
- if (selectionRect) {
245
- hoverColumns(getSelectedColumnIndexes(selectionRect), true)(
246
- state,
247
- dispatch,
248
- );
249
- return true;
250
- }
251
- return false;
252
- },
251
+ onFocus: highlightColumnsHandler,
252
+ onBlur: clearHoverSelection(),
253
+ onMouseOver: highlightColumnsHandler,
253
254
  onMouseLeave: clearHoverSelection(),
254
255
  selected: false,
255
256
  disabled: false,
@@ -270,17 +271,9 @@ export const getToolbarCellOptionsConfig = (
270
271
  }
271
272
  return true;
272
273
  },
273
- onMouseOver: (state: EditorState, dispatch?: CommandDispatch) => {
274
- const selectionRect = getClosestSelectionRect(state);
275
- if (selectionRect) {
276
- hoverRows(getSelectedRowIndexes(selectionRect), true)(
277
- state,
278
- dispatch,
279
- );
280
- return true;
281
- }
282
- return false;
283
- },
274
+ onFocus: highlightRowsHandler,
275
+ onBlur: clearHoverSelection(),
276
+ onMouseOver: highlightRowsHandler,
284
277
  onMouseLeave: clearHoverSelection(),
285
278
  selected: false,
286
279
  disabled: false,
@@ -396,6 +389,7 @@ export const getToolbarCellOptionsConfig = (
396
389
 
397
390
  return {
398
391
  id: 'editor.table.cellOptions',
392
+ testId: 'cell_options',
399
393
  type: 'dropdown',
400
394
  title: formatMessage(tableMessages.cellOptions),
401
395
  options,
@@ -456,17 +450,28 @@ export const getToolbarConfig =
456
450
 
457
451
  // Check if we need to show confirm dialog for delete button
458
452
  let confirmDialog;
459
- const localId: string | undefined = tableObject.node.attrs.localId;
460
453
 
461
- if (localId && isReferencedSource(state, localId)) {
462
- confirmDialog = {
454
+ if (isReferencedSource(state, tableObject.node)) {
455
+ confirmDialog = (): ConfirmDialogOptions => ({
456
+ title: intl.formatMessage(tableMessages.deleteElementTitle),
463
457
  okButtonLabel: intl.formatMessage(
464
458
  tableMessages.confirmDeleteLinkedModalOKButton,
465
459
  ),
466
460
  message: intl.formatMessage(
467
461
  tableMessages.confirmDeleteLinkedModalMessage,
462
+ { nodeName: getNodeName(state, tableObject.node) },
463
+ ),
464
+ messagePrefix: intl.formatMessage(
465
+ tableMessages.confirmDeleteLinkedModalMessagePrefix,
468
466
  ),
469
- };
467
+ isReferentialityDialog: true,
468
+ getChildrenInfo: () => getChildrenInfo(state, tableObject.node),
469
+ checkboxLabel: intl.formatMessage(
470
+ tableMessages.confirmModalCheckboxLabel,
471
+ ),
472
+ onConfirm: (isChecked = false) =>
473
+ clickWithCheckboxHandler(isChecked, tableObject.node),
474
+ });
470
475
  }
471
476
 
472
477
  const getDomRef = (editorView: EditorView) => {
@@ -516,6 +521,8 @@ export const getToolbarConfig =
516
521
  nodeType,
517
522
  onMouseEnter: hoverTable(false, true),
518
523
  onMouseLeave: clearHoverSelection(),
524
+ onFocus: hoverTable(false, true),
525
+ onBlur: clearHoverSelection(),
519
526
  },
520
527
  { type: 'separator' },
521
528
  ],
@@ -528,8 +535,11 @@ export const getToolbarConfig =
528
535
  onClick: deleteTableWithAnalytics(editorAnalyticsAPI),
529
536
  disabled: !!resizeState && !!resizeState.dragging,
530
537
  onMouseEnter: hoverTable(true),
538
+ onFocus: hoverTable(true),
539
+ onBlur: clearHoverSelection(),
531
540
  onMouseLeave: clearHoverSelection(),
532
541
  title: intl.formatMessage(commonMessages.remove),
542
+ focusEditoronEnter: true,
533
543
  confirmDialog,
534
544
  },
535
545
  ],
@@ -604,7 +614,7 @@ const getColorPicker = (
604
614
 
605
615
  return [
606
616
  {
607
- id: 'editor.panel.colorPicker',
617
+ id: 'editor.table.colorPicker',
608
618
  title: formatMessage(ContextualMenuMessages.cellBackground),
609
619
  type: 'select',
610
620
  selectType: 'color',
@@ -620,3 +630,49 @@ const getColorPicker = (
620
630
  separator(menu.hidden),
621
631
  ];
622
632
  };
633
+
634
+ const clickWithCheckboxHandler =
635
+ (
636
+ isChecked: boolean,
637
+ node?: PMNode,
638
+ editorAnalyticsAPI?: EditorAnalyticsAPI | undefined | null,
639
+ ): Command =>
640
+ (state, dispatch) => {
641
+ if (!node) {
642
+ return false;
643
+ }
644
+
645
+ if (!isChecked) {
646
+ return deleteTableWithAnalytics(editorAnalyticsAPI)(state, dispatch);
647
+ } else {
648
+ removeDescendantNodes(node)(state, dispatch);
649
+ }
650
+ return true;
651
+ };
652
+
653
+ const highlightRowsHandler = (
654
+ state: EditorState,
655
+ dispatch?: CommandDispatch,
656
+ ) => {
657
+ const selectionRect = getClosestSelectionRect(state);
658
+ if (selectionRect) {
659
+ hoverRows(getSelectedRowIndexes(selectionRect), true)(state, dispatch);
660
+ return true;
661
+ }
662
+ return false;
663
+ };
664
+
665
+ const highlightColumnsHandler = (
666
+ state: EditorState,
667
+ dispatch?: CommandDispatch,
668
+ ) => {
669
+ const selectionRect = getClosestSelectionRect(state);
670
+ if (selectionRect) {
671
+ hoverColumns(getSelectedColumnIndexes(selectionRect), true)(
672
+ state,
673
+ dispatch,
674
+ );
675
+ return true;
676
+ }
677
+ return false;
678
+ };
@@ -4,6 +4,7 @@ import {
4
4
  tableMarginTop,
5
5
  tableSharedStyle,
6
6
  } from '@atlaskit/editor-common/styles';
7
+ import type { FeatureFlags } from '@atlaskit/editor-common/types';
7
8
  import { fontSize } from '@atlaskit/theme/constants';
8
9
  import { N40A, B300, N300, N20A, N0, R500 } from '@atlaskit/theme/colors';
9
10
  import {
@@ -112,8 +113,18 @@ const sentinelStyles = `.${ClassName.TABLE_CONTAINER} {
112
113
  }
113
114
  }`;
114
115
 
116
+ // previous styles to add spacing to numbered lists with
117
+ // large item markers (e.g. 101, 102, ...) when nested inside tables
118
+ const listLargeNumericMarkersOldStyles = `
119
+ .ProseMirror .pm-table-cell-content-wrap ol[data-child-count='100+'] {
120
+ padding-left: revert;
121
+ }
122
+ `;
123
+
115
124
  // TODO: https://product-fabric.atlassian.net/browse/DSP-4139
116
- export const tableStyles = (props: ThemeProps) => css`
125
+ export const tableStyles = (
126
+ props: ThemeProps & { featureFlags?: FeatureFlags },
127
+ ) => css`
117
128
  .${ClassName.LAYOUT_BUTTON} button {
118
129
  background: ${token('color.background.neutral', N20A)};
119
130
  color: ${token('color.icon', N300)};
@@ -713,9 +724,14 @@ export const tableStyles = (props: ThemeProps) => css`
713
724
  cursor: col-resize;
714
725
  }
715
726
 
716
- .ProseMirror .pm-table-cell-content-wrap ol[data-child-count='100+'] {
717
- padding-left: revert;
718
- }
727
+ /*
728
+ ED-15882: When custom start numbers is enabled for lists, we have
729
+ styles that handle this generally (in editor-common) so we can
730
+ throw away the older table-specific styles here.
731
+ */
732
+ ${props?.featureFlags?.restartNumberedLists
733
+ ? ``
734
+ : listLargeNumericMarkersOldStyles}
719
735
  `;
720
736
 
721
737
  export const tableFullPageEditorStyles = css`
@@ -29,15 +29,30 @@ export default defineMessages({
29
29
 
30
30
  confirmDeleteLinkedModalOKButton: {
31
31
  id: 'fabric.editor.tables.confirmDeleteLinkedModalOKButton',
32
- defaultMessage: 'Remove table and data',
32
+ defaultMessage: 'Delete',
33
33
  description:
34
34
  'Action button label for confirm modal when deleting a table linked to an extension.',
35
35
  },
36
36
  confirmDeleteLinkedModalMessage: {
37
37
  id: 'fabric.editor.tables.confirmDeleteLinkedModalMessage',
38
- defaultMessage:
39
- 'Removing this table will break all the charts connected to it.',
38
+ defaultMessage: 'Deleting {nodeName} will break anything connected to it.',
40
39
  description:
41
40
  'Message for confirm modal when deleting a table linked to an extension.',
42
41
  },
42
+ confirmDeleteLinkedModalMessagePrefix: {
43
+ id: 'fabric.editor.extension.confirmDeleteLinkedModalMessagePrefix',
44
+ defaultMessage: 'Deleting',
45
+ description: 'prefix for confirmation dialog text',
46
+ },
47
+ confirmModalCheckboxLabel: {
48
+ id: 'fabric.editor.floatingToolbar.confirmModalCheckboxLabel',
49
+ defaultMessage: 'Also delete connected elements',
50
+ description: 'checkbox label text',
51
+ },
52
+ deleteElementTitle: {
53
+ id: 'fabric.editor.extension.deleteElementTitle',
54
+ defaultMessage: 'Delete element',
55
+ description:
56
+ 'Title text for confirm modal when deleting an extension linked to a data consumer.',
57
+ },
43
58
  });