@atlaskit/editor-plugin-table 7.18.2 → 7.18.4

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 (75) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/commands/column-resize.js +0 -12
  3. package/dist/cjs/commands/go-to-next-cell.js +8 -11
  4. package/dist/cjs/commands/index.js +6 -0
  5. package/dist/cjs/commands/misc.js +15 -1
  6. package/dist/cjs/commands/selection.js +4 -11
  7. package/dist/cjs/event-handlers.js +7 -11
  8. package/dist/cjs/plugin.js +7 -4
  9. package/dist/cjs/pm-plugins/keymap.js +22 -21
  10. package/dist/cjs/pm-plugins/main.js +26 -30
  11. package/dist/cjs/pm-plugins/table-resizing/event-handlers.js +13 -21
  12. package/dist/cjs/pm-plugins/table-resizing/plugin.js +8 -11
  13. package/dist/cjs/reducer.js +1 -0
  14. package/dist/cjs/ui/FloatingContextualButton/index.js +18 -2
  15. package/dist/cjs/ui/FloatingContextualMenu/ContextualMenu.js +170 -35
  16. package/dist/cjs/ui/FloatingContextualMenu/index.js +4 -2
  17. package/dist/es2019/commands/column-resize.js +0 -12
  18. package/dist/es2019/commands/go-to-next-cell.js +8 -11
  19. package/dist/es2019/commands/index.js +1 -1
  20. package/dist/es2019/commands/misc.js +9 -1
  21. package/dist/es2019/commands/selection.js +4 -11
  22. package/dist/es2019/event-handlers.js +8 -12
  23. package/dist/es2019/plugin.js +7 -4
  24. package/dist/es2019/pm-plugins/keymap.js +24 -23
  25. package/dist/es2019/pm-plugins/main.js +27 -31
  26. package/dist/es2019/pm-plugins/table-resizing/event-handlers.js +13 -21
  27. package/dist/es2019/pm-plugins/table-resizing/plugin.js +9 -12
  28. package/dist/es2019/reducer.js +1 -0
  29. package/dist/es2019/ui/FloatingContextualButton/index.js +17 -2
  30. package/dist/es2019/ui/FloatingContextualMenu/ContextualMenu.js +159 -24
  31. package/dist/es2019/ui/FloatingContextualMenu/index.js +4 -2
  32. package/dist/esm/commands/column-resize.js +0 -12
  33. package/dist/esm/commands/go-to-next-cell.js +8 -11
  34. package/dist/esm/commands/index.js +1 -1
  35. package/dist/esm/commands/misc.js +14 -0
  36. package/dist/esm/commands/selection.js +4 -11
  37. package/dist/esm/event-handlers.js +7 -11
  38. package/dist/esm/plugin.js +7 -4
  39. package/dist/esm/pm-plugins/keymap.js +24 -23
  40. package/dist/esm/pm-plugins/main.js +26 -30
  41. package/dist/esm/pm-plugins/table-resizing/event-handlers.js +13 -21
  42. package/dist/esm/pm-plugins/table-resizing/plugin.js +8 -11
  43. package/dist/esm/reducer.js +1 -0
  44. package/dist/esm/ui/FloatingContextualButton/index.js +15 -2
  45. package/dist/esm/ui/FloatingContextualMenu/ContextualMenu.js +171 -40
  46. package/dist/esm/ui/FloatingContextualMenu/index.js +4 -2
  47. package/dist/types/commands/index.d.ts +1 -1
  48. package/dist/types/commands/misc.d.ts +1 -0
  49. package/dist/types/types.d.ts +6 -0
  50. package/dist/types/ui/FloatingContextualButton/index.d.ts +1 -0
  51. package/dist/types/ui/FloatingContextualMenu/ContextualMenu.d.ts +7 -3
  52. package/dist/types/ui/FloatingContextualMenu/index.d.ts +2 -1
  53. package/dist/types-ts4.5/commands/index.d.ts +1 -1
  54. package/dist/types-ts4.5/commands/misc.d.ts +1 -0
  55. package/dist/types-ts4.5/types.d.ts +6 -0
  56. package/dist/types-ts4.5/ui/FloatingContextualButton/index.d.ts +1 -0
  57. package/dist/types-ts4.5/ui/FloatingContextualMenu/ContextualMenu.d.ts +7 -3
  58. package/dist/types-ts4.5/ui/FloatingContextualMenu/index.d.ts +2 -1
  59. package/package.json +4 -4
  60. package/src/commands/column-resize.ts +0 -14
  61. package/src/commands/go-to-next-cell.ts +7 -10
  62. package/src/commands/index.ts +1 -0
  63. package/src/commands/misc.ts +13 -0
  64. package/src/commands/selection.ts +4 -11
  65. package/src/event-handlers.ts +6 -12
  66. package/src/plugin.tsx +6 -1
  67. package/src/pm-plugins/keymap.ts +65 -62
  68. package/src/pm-plugins/main.ts +27 -31
  69. package/src/pm-plugins/table-resizing/event-handlers.ts +11 -19
  70. package/src/pm-plugins/table-resizing/plugin.ts +7 -10
  71. package/src/reducer.ts +1 -0
  72. package/src/types.ts +8 -1
  73. package/src/ui/FloatingContextualButton/index.tsx +19 -1
  74. package/src/ui/FloatingContextualMenu/ContextualMenu.tsx +207 -30
  75. package/src/ui/FloatingContextualMenu/index.tsx +3 -0
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable @atlaskit/design-system/prefer-primitives */
2
2
  /** @jsx jsx */
3
- import { Component } from 'react';
3
+ import React, { Component } from 'react';
4
+ import type { PointerEvent } from 'react';
4
5
 
5
6
  // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
6
7
  import { jsx } from '@emotion/react';
@@ -24,9 +25,14 @@ import {
24
25
  backgroundPaletteTooltipMessages,
25
26
  cellBackgroundColorPalette,
26
27
  ColorPalette,
28
+ getSelectedRowAndColumnFromPalette,
27
29
  } from '@atlaskit/editor-common/ui-color';
28
30
  import type { MenuItem } from '@atlaskit/editor-common/ui-menu';
29
- import { ArrowKeyNavigationType, DropdownMenu } from '@atlaskit/editor-common/ui-menu';
31
+ import {
32
+ ArrowKeyNavigationProvider,
33
+ ArrowKeyNavigationType,
34
+ DropdownMenu,
35
+ } from '@atlaskit/editor-common/ui-menu';
30
36
  import { closestElement } from '@atlaskit/editor-common/utils';
31
37
  import { hexToEditorBackgroundPaletteColor } from '@atlaskit/editor-palette';
32
38
  import type { EditorView } from '@atlaskit/editor-prosemirror/view';
@@ -43,6 +49,7 @@ import {
43
49
  hoverColumns,
44
50
  hoverMergedCells,
45
51
  hoverRows,
52
+ setFocusToCellMenu,
46
53
  toggleContextualMenu,
47
54
  } from '../../commands';
48
55
  import {
@@ -58,6 +65,7 @@ import {
58
65
  splitCellWithAnalytics,
59
66
  } from '../../commands-with-analytics';
60
67
  import { getPluginState } from '../../pm-plugins/plugin-factory';
68
+ import { pluginKey as tablePluginKey } from '../../pm-plugins/plugin-key';
61
69
  import { getNewResizeStateFromSelectedColumns } from '../../pm-plugins/table-resizing/utils/resize-state';
62
70
  import { canMergeCells } from '../../transforms';
63
71
  import { TableCssClassName as ClassName } from '../../types';
@@ -66,7 +74,11 @@ import {
66
74
  getSelectedColumnIndexes,
67
75
  getSelectedRowIndexes,
68
76
  } from '../../utils';
69
- import { contextualMenuDropdownWidth, contextualMenuDropdownWidthDnD } from '../consts';
77
+ import {
78
+ colorPalletteColumns,
79
+ contextualMenuDropdownWidth,
80
+ contextualMenuDropdownWidthDnD,
81
+ } from '../consts';
70
82
  import { AddColRightIcon, AddRowBelowIcon, MergeCellsIcon, SplitCellIcon } from '../icons';
71
83
 
72
84
  import { cellColourPreviewStyles, elementBeforeIconStyles } from './styles';
@@ -85,40 +97,77 @@ export interface Props {
85
97
  editorAnalyticsAPI?: EditorAnalyticsAPI;
86
98
  getEditorContainerWidth: GetEditorContainerWidth;
87
99
  getEditorFeatureFlags?: GetEditorFeatureFlags;
100
+ isCellMenuOpenByKeyboard?: boolean;
88
101
  }
89
102
 
90
103
  export interface State {
91
104
  isSubmenuOpen: boolean;
105
+ isOpenAllowed: boolean;
92
106
  }
93
-
107
+ const arrowsList = new Set(['ArrowRight', 'ArrowLeft']);
94
108
  export class ContextualMenu extends Component<Props & WrappedComponentProps, State> {
95
109
  state: State = {
96
110
  isSubmenuOpen: false,
111
+ isOpenAllowed: false,
97
112
  };
98
113
 
99
114
  static defaultProps = {
100
115
  boundariesElement: typeof document !== 'undefined' ? document.body : undefined,
101
116
  };
117
+ private dropdownMenuRef = React.createRef<HTMLDivElement>();
118
+
119
+ componentDidMount() {
120
+ if (getBooleanFF('platform.editor.a11y-table-context-menu_y4c9c')) {
121
+ // ArrowKeyNavigationProvider in DropdownMenu expects that menu handle will stay focused
122
+ // until user pressed ArrowDown.
123
+ // Behavior above fails the A11Y requirement about first item in menu should be focused immediately.
124
+ // so here is triggering componentDidUpdate inside dropdown to set focus on first element
125
+ const { isCellMenuOpenByKeyboard } = this.props;
126
+ if (isCellMenuOpenByKeyboard) {
127
+ this.setState({
128
+ ...this.state,
129
+ isOpenAllowed: isCellMenuOpenByKeyboard,
130
+ });
131
+ }
132
+ }
133
+ }
102
134
 
103
135
  render() {
104
- const { isOpen, mountPoint, offset, boundariesElement, editorView } = this.props;
136
+ const { isOpen, mountPoint, offset, boundariesElement, editorView, isCellMenuOpenByKeyboard } =
137
+ this.props;
105
138
  const { isDragAndDropEnabled } = getPluginState(editorView.state);
106
139
  const items = isDragAndDropEnabled
107
140
  ? this.createNewContextMenuItems()
108
141
  : this.createOriginalContextMenuItems();
142
+ let isOpenAllowed = false;
143
+
144
+ if (getBooleanFF('platform.editor.a11y-table-context-menu_y4c9c')) {
145
+ isOpenAllowed = isCellMenuOpenByKeyboard ? this.state.isOpenAllowed : isOpen;
146
+ } else {
147
+ isOpenAllowed = isOpen;
148
+ }
109
149
 
110
150
  return (
111
- <div data-testid="table-cell-contextual-menu" onMouseLeave={this.closeSubmenu}>
151
+ <div
152
+ data-testid="table-cell-contextual-menu"
153
+ onMouseLeave={this.closeSubmenu}
154
+ ref={this.dropdownMenuRef}
155
+ >
112
156
  <DropdownMenu
113
157
  mountTo={mountPoint}
114
158
  //This needs be removed when the a11y is completely handled
115
159
  //Disabling key navigation now as it works only partially
116
160
  arrowKeyNavigationProviderOptions={{
117
161
  type: ArrowKeyNavigationType.MENU,
118
- disableArrowKeyNavigation: true,
162
+ disableArrowKeyNavigation:
163
+ getBooleanFF('platform.editor.a11y-table-context-menu_y4c9c') &&
164
+ isCellMenuOpenByKeyboard &&
165
+ !this.state.isSubmenuOpen
166
+ ? false
167
+ : true,
119
168
  }}
120
169
  items={items}
121
- isOpen={isOpen}
170
+ isOpen={isOpenAllowed}
122
171
  onOpenChange={this.handleOpenChange}
123
172
  onItemActivated={this.onMenuItemActivated}
124
173
  onMouseEnter={this.handleItemMouseEnter}
@@ -127,9 +176,21 @@ export class ContextualMenu extends Component<Props & WrappedComponentProps, Sta
127
176
  fitWidth={
128
177
  isDragAndDropEnabled ? contextualMenuDropdownWidthDnD : contextualMenuDropdownWidth
129
178
  }
179
+ shouldFocusFirstItem={
180
+ getBooleanFF('platform.editor.a11y-table-context-menu_y4c9c')
181
+ ? () => {
182
+ return Boolean(isCellMenuOpenByKeyboard);
183
+ }
184
+ : undefined
185
+ }
130
186
  boundariesElement={boundariesElement}
131
187
  offset={offset}
132
188
  section={isDragAndDropEnabled ? { hasSeparator: true } : undefined}
189
+ isAllowEnterDefaultBehavior={
190
+ getBooleanFF('platform.editor.a11y-table-context-menu_y4c9c')
191
+ ? this.state.isSubmenuOpen
192
+ : false
193
+ }
133
194
  />
134
195
  </div>
135
196
  );
@@ -157,6 +218,7 @@ export class ContextualMenu extends Component<Props & WrappedComponentProps, Sta
157
218
  isOpen,
158
219
  intl: { formatMessage },
159
220
  editorView,
221
+ isCellMenuOpenByKeyboard,
160
222
  } = this.props;
161
223
  const { isSubmenuOpen } = this.state;
162
224
  const { targetCellPosition, isDragAndDropEnabled } = getPluginState(editorView.state);
@@ -164,6 +226,18 @@ export class ContextualMenu extends Component<Props & WrappedComponentProps, Sta
164
226
  if (allowBackgroundColor) {
165
227
  const node = isOpen && targetCellPosition ? state.doc.nodeAt(targetCellPosition) : null;
166
228
  const background = hexToEditorBackgroundPaletteColor(node?.attrs?.background || '#ffffff');
229
+ let selectedRowIndex;
230
+ let selectedColumnIndex;
231
+
232
+ if (getBooleanFF('platform.editor.a11y-table-context-menu_y4c9c')) {
233
+ const selectedRowAndColumnFromPalette = getSelectedRowAndColumnFromPalette(
234
+ cellBackgroundColorPalette,
235
+ background!,
236
+ colorPalletteColumns,
237
+ );
238
+ selectedRowIndex = selectedRowAndColumnFromPalette.selectedRowIndex;
239
+ selectedColumnIndex = selectedRowAndColumnFromPalette.selectedColumnIndex;
240
+ }
167
241
  return {
168
242
  content: isDragAndDropEnabled
169
243
  ? formatMessage(messages.backgroundColor)
@@ -191,26 +265,64 @@ export class ContextualMenu extends Component<Props & WrappedComponentProps, Sta
191
265
  : ClassName.CONTEXTUAL_MENU_ICON
192
266
  }
193
267
  />
194
- {isSubmenuOpen && (
195
- <div
196
- // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
197
- className={ClassName.CONTEXTUAL_SUBMENU}
198
- ref={this.handleSubMenuRef}
199
- >
200
- <ColorPalette
201
- cols={7}
202
- onClick={this.setColor}
203
- selectedColor={node?.attrs?.background || '#ffffff'}
204
- paletteOptions={{
205
- palette: cellBackgroundColorPalette,
206
- paletteColorTooltipMessages: backgroundPaletteTooltipMessages,
207
- hexToPaletteColor: hexToEditorBackgroundPaletteColor,
208
- }}
209
- />
210
- </div>
211
- )}
268
+ {getBooleanFF('platform.editor.a11y-table-context-menu_y4c9c')
269
+ ? isSubmenuOpen && (
270
+ <div
271
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
272
+ className={ClassName.CONTEXTUAL_SUBMENU}
273
+ ref={this.handleSubMenuRef}
274
+ >
275
+ <ArrowKeyNavigationProvider
276
+ type={ArrowKeyNavigationType.COLOR}
277
+ selectedRowIndex={selectedRowIndex || 0}
278
+ selectedColumnIndex={selectedColumnIndex || 0}
279
+ handleClose={() => {
280
+ this.setState({ isSubmenuOpen: false });
281
+ if (this.dropdownMenuRef && this.dropdownMenuRef.current) {
282
+ const focusableItems = this.dropdownMenuRef.current.querySelectorAll(
283
+ 'div[tabindex="-1"]:not([disabled])',
284
+ ) as NodeListOf<HTMLElement>;
285
+ if (focusableItems && focusableItems.length) {
286
+ focusableItems[0].focus();
287
+ }
288
+ }
289
+ }}
290
+ isPopupPositioned={true}
291
+ isOpenedByKeyboard={isCellMenuOpenByKeyboard!}
292
+ >
293
+ <ColorPalette
294
+ cols={7}
295
+ onClick={this.setColor}
296
+ selectedColor={node?.attrs?.background || '#ffffff'}
297
+ paletteOptions={{
298
+ palette: cellBackgroundColorPalette,
299
+ paletteColorTooltipMessages: backgroundPaletteTooltipMessages,
300
+ hexToPaletteColor: hexToEditorBackgroundPaletteColor,
301
+ }}
302
+ />
303
+ </ArrowKeyNavigationProvider>
304
+ </div>
305
+ )
306
+ : isSubmenuOpen && (
307
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
308
+ <div className={ClassName.CONTEXTUAL_SUBMENU} ref={this.handleSubMenuRef}>
309
+ <ColorPalette
310
+ cols={7}
311
+ onClick={this.setColor}
312
+ selectedColor={node?.attrs?.background || '#ffffff'}
313
+ paletteOptions={{
314
+ palette: cellBackgroundColorPalette,
315
+ paletteColorTooltipMessages: backgroundPaletteTooltipMessages,
316
+ hexToPaletteColor: hexToEditorBackgroundPaletteColor,
317
+ }}
318
+ />
319
+ </div>
320
+ )}
212
321
  </div>
213
322
  ),
323
+ 'aria-expanded': getBooleanFF('platform.editor.a11y-table-context-menu_y4c9c')
324
+ ? isSubmenuOpen
325
+ : undefined,
214
326
  } as MenuItem;
215
327
  }
216
328
  };
@@ -526,6 +638,7 @@ export class ContextualMenu extends Component<Props & WrappedComponentProps, Sta
526
638
  editorAnalyticsAPI,
527
639
  getEditorContainerWidth,
528
640
  getEditorFeatureFlags,
641
+ isCellMenuOpenByKeyboard,
529
642
  } = this.props;
530
643
  // TargetCellPosition could be outdated: https://product-fabric.atlassian.net/browse/ED-8129
531
644
  const { state, dispatch } = editorView;
@@ -533,6 +646,24 @@ export class ContextualMenu extends Component<Props & WrappedComponentProps, Sta
533
646
 
534
647
  const { tableDuplicateCellColouring = false, tableWithFixedColumnWidthsOption = false } =
535
648
  getEditorFeatureFlags ? getEditorFeatureFlags() : {};
649
+ // context menu opened by keyboard and any item except 'background' activated
650
+ // or color has been chosen from color palette
651
+ if (
652
+ getBooleanFF('platform.editor.a11y-table-context-menu_y4c9c') &&
653
+ isCellMenuOpenByKeyboard &&
654
+ (item.value.name !== 'background' ||
655
+ (item.value.name === 'background' && this.state.isSubmenuOpen))
656
+ ) {
657
+ const { tr } = state;
658
+ tr.setMeta(tablePluginKey, {
659
+ type: 'SET_CELL_MENU_OPEN',
660
+ data: {
661
+ isCellMenuOpenByKeyboard: false,
662
+ },
663
+ });
664
+ dispatch(tr);
665
+ editorView.dom.focus(); // otherwise cursor disappears from cell
666
+ }
536
667
 
537
668
  const shouldUseIncreasedScalingPercent =
538
669
  isTableScalingEnabled &&
@@ -630,6 +761,20 @@ export class ContextualMenu extends Component<Props & WrappedComponentProps, Sta
630
761
  )(state, dispatch);
631
762
  this.toggleOpen();
632
763
  break;
764
+ case 'background': {
765
+ // This is called twice.
766
+ // 1st time when user chooses the background color item.
767
+ // 2nd when color has been chosen from color palette.
768
+ // here we are handling the 1st call relying on the isSubmenuOpen state value
769
+ if (
770
+ getBooleanFF('platform.editor.a11y-table-context-menu_y4c9c') &&
771
+ isCellMenuOpenByKeyboard &&
772
+ !this.state.isSubmenuOpen
773
+ ) {
774
+ this.setState({ isSubmenuOpen: true });
775
+ }
776
+ break;
777
+ }
633
778
  }
634
779
  };
635
780
 
@@ -646,12 +791,44 @@ export class ContextualMenu extends Component<Props & WrappedComponentProps, Sta
646
791
  }
647
792
  };
648
793
 
649
- private handleOpenChange = () => {
794
+ private handleOpenChange = (payload?: {
795
+ event: PointerEvent | KeyboardEvent;
796
+ isOpen: boolean;
797
+ }) => {
650
798
  const {
651
- editorView: { state, dispatch },
799
+ editorView: { state, dispatch, dom },
800
+ isCellMenuOpenByKeyboard,
652
801
  } = this.props;
653
- toggleContextualMenu()(state, dispatch);
654
- this.setState({ isSubmenuOpen: false });
802
+
803
+ if (getBooleanFF('platform.editor.a11y-table-context-menu_y4c9c')) {
804
+ if (payload) {
805
+ const { event } = payload;
806
+ if (event && event instanceof KeyboardEvent) {
807
+ if (!this.state.isSubmenuOpen) {
808
+ if (arrowsList.has(event.key)) {
809
+ // preventing default behavior for avoiding cursor jump to next/previous table column
810
+ // when left/right arrow pressed.
811
+ event.preventDefault();
812
+ }
813
+
814
+ toggleContextualMenu()(state, dispatch);
815
+ this.setState({ isSubmenuOpen: false });
816
+ setFocusToCellMenu(false)(state, dispatch);
817
+ dom.focus();
818
+ }
819
+ } else {
820
+ // mouse click outside
821
+ toggleContextualMenu()(state, dispatch);
822
+ this.setState({ isSubmenuOpen: false });
823
+ if (isCellMenuOpenByKeyboard) {
824
+ setFocusToCellMenu(false)(state, dispatch);
825
+ }
826
+ }
827
+ }
828
+ } else {
829
+ toggleContextualMenu()(state, dispatch);
830
+ this.setState({ isSubmenuOpen: false });
831
+ }
655
832
  };
656
833
 
657
834
  private handleItemMouseEnter = ({ item }: { item: any }) => {
@@ -40,6 +40,7 @@ export interface Props {
40
40
  scrollableElement?: HTMLElement;
41
41
  pluginConfig?: PluginConfig;
42
42
  editorAnalyticsAPI?: EditorAnalyticsAPI;
43
+ isCellMenuOpenByKeyboard?: boolean;
43
44
  }
44
45
 
45
46
  const FloatingContextualMenu = ({
@@ -52,6 +53,7 @@ const FloatingContextualMenu = ({
52
53
  editorAnalyticsAPI,
53
54
  getEditorContainerWidth,
54
55
  getEditorFeatureFlags,
56
+ isCellMenuOpenByKeyboard,
55
57
  }: Props) => {
56
58
  // TargetCellPosition could be outdated: https://product-fabric.atlassian.net/browse/ED-8129
57
59
  const { targetCellPosition, isDragAndDropEnabled } = getPluginState(editorView.state);
@@ -108,6 +110,7 @@ const FloatingContextualMenu = ({
108
110
  editorAnalyticsAPI={editorAnalyticsAPI}
109
111
  getEditorContainerWidth={getEditorContainerWidth}
110
112
  getEditorFeatureFlags={getEditorFeatureFlags}
113
+ isCellMenuOpenByKeyboard={isCellMenuOpenByKeyboard}
111
114
  />
112
115
  </div>
113
116
  </Popup>