From dfe994b3419e57a878b1f1d8935e0af5c471c0ce Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 30 Dec 2024 17:56:43 +0800 Subject: [PATCH] feat: auto-dismiss collapsed handle on Android if no interaction occurs (#7088) * feat: support auto-dismiss collapsed handle on Android * fix: hit test area of collasepd handle is too big * chore: upgrade appflowy_editor * fix: simple table issues on mobile * feat: highlight cell after insertion * test: text color and cell background color test * fix: sign_in_page_settings_test --- .../settings/sign_in_page_settings_test.dart | 1 + .../document/presentation/editor_page.dart | 4 + .../simple_table_map_operation.dart | 241 ++++++++++++++++++ .../simple_table_more_action_popup.dart | 54 ++++ .../document/presentation/editor_style.dart | 3 +- frontend/appflowy_flutter/pubspec.lock | 4 +- frontend/appflowy_flutter/pubspec.yaml | 2 +- .../simple_table_delete_operation_test.dart | 62 +++++ ...simple_table_duplicate_operation_test.dart | 64 +++++ .../simple_table_insert_operation_test.dart | 62 +++++ 10 files changed, 493 insertions(+), 4 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/desktop/settings/sign_in_page_settings_test.dart b/frontend/appflowy_flutter/integration_test/desktop/settings/sign_in_page_settings_test.dart index b7074be357..b0b751a52f 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/settings/sign_in_page_settings_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/settings/sign_in_page_settings_test.dart @@ -67,6 +67,7 @@ void main() { // open settings page to check the result await tester.tapButton(settingsButton); + await tester.pumpAndSettle(const Duration(milliseconds: 250)); // check the server type expect( diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart index 0cf39e8bcc..d83bd055ce 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart @@ -505,6 +505,10 @@ class _AppFlowyEditorPageState extends State Position(path: lastNode.path), ); } + + transaction.customSelectionType = SelectionType.inline; + transaction.reason = SelectionUpdateReason.uiEvent; + await editorState.apply(transaction); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart index cb42571704..875da5fffe 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart @@ -104,6 +104,18 @@ extension TableMapOperation on Node { comparator: (iKey, index) => iKey >= index, ); + final rowBoldAttributes = _remapSource( + this.rowBoldAttributes, + index, + comparator: (iKey, index) => iKey >= index, + ); + + final rowTextColors = _remapSource( + this.rowTextColors, + index, + comparator: (iKey, index) => iKey >= index, + ); + return attributes .mergeValues( SimpleTableBlockKeys.rowColors, @@ -112,6 +124,14 @@ extension TableMapOperation on Node { .mergeValues( SimpleTableBlockKeys.rowAligns, rowAligns, + ) + .mergeValues( + SimpleTableBlockKeys.rowBoldAttributes, + rowBoldAttributes, + ) + .mergeValues( + SimpleTableBlockKeys.rowTextColors, + rowTextColors, ); } catch (e) { Log.warn('Failed to map row insertion attributes: $e'); @@ -167,6 +187,18 @@ extension TableMapOperation on Node { comparator: (iKey, index) => iKey >= index, ); + final columnBoldAttributes = _remapSource( + this.columnBoldAttributes, + index, + comparator: (iKey, index) => iKey >= index, + ); + + final columnTextColors = _remapSource( + this.columnTextColors, + index, + comparator: (iKey, index) => iKey >= index, + ); + final bool distributeColumnWidthsEvenly = attributes[SimpleTableBlockKeys.distributeColumnWidthsEvenly] ?? false; @@ -189,6 +221,14 @@ extension TableMapOperation on Node { .mergeValues( SimpleTableBlockKeys.columnWidths, columnWidths, + ) + .mergeValues( + SimpleTableBlockKeys.columnBoldAttributes, + columnBoldAttributes, + ) + .mergeValues( + SimpleTableBlockKeys.columnTextColors, + columnTextColors, ); } catch (e) { Log.warn('Failed to map row insertion attributes: $e'); @@ -238,6 +278,18 @@ extension TableMapOperation on Node { index, ); + final (rowBoldAttributes, duplicatedRowBoldAttribute) = + _findDuplicatedEntryAndRemap( + this.rowBoldAttributes, + index, + ); + + final (rowTextColors, duplicatedRowTextColor) = + _findDuplicatedEntryAndRemap( + this.rowTextColors, + index, + ); + return attributes .mergeValues( SimpleTableBlockKeys.rowColors, @@ -248,6 +300,16 @@ extension TableMapOperation on Node { SimpleTableBlockKeys.rowAligns, rowAligns, duplicatedEntry: duplicatedRowAlign, + ) + .mergeValues( + SimpleTableBlockKeys.rowBoldAttributes, + rowBoldAttributes, + duplicatedEntry: duplicatedRowBoldAttribute, + ) + .mergeValues( + SimpleTableBlockKeys.rowTextColors, + rowTextColors, + duplicatedEntry: duplicatedRowTextColor, ); } catch (e) { Log.warn('Failed to map row insertion attributes: $e'); @@ -304,6 +366,18 @@ extension TableMapOperation on Node { index, ); + final (columnBoldAttributes, duplicatedColumnBoldAttribute) = + _findDuplicatedEntryAndRemap( + this.columnBoldAttributes, + index, + ); + + final (columnTextColors, duplicatedColumnTextColor) = + _findDuplicatedEntryAndRemap( + this.columnTextColors, + index, + ); + return attributes .mergeValues( SimpleTableBlockKeys.columnColors, @@ -319,6 +393,16 @@ extension TableMapOperation on Node { SimpleTableBlockKeys.columnWidths, columnWidths, duplicatedEntry: duplicatedColumnWidth, + ) + .mergeValues( + SimpleTableBlockKeys.columnBoldAttributes, + columnBoldAttributes, + duplicatedEntry: duplicatedColumnBoldAttribute, + ) + .mergeValues( + SimpleTableBlockKeys.columnTextColors, + columnTextColors, + duplicatedEntry: duplicatedColumnTextColor, ); } catch (e) { Log.warn('Failed to map column duplication attributes: $e'); @@ -364,6 +448,7 @@ extension TableMapOperation on Node { comparator: (iKey, index) => iKey > index, filterIndex: index, ); + final columnAligns = _remapSource( this.columnAligns, index, @@ -371,6 +456,7 @@ extension TableMapOperation on Node { comparator: (iKey, index) => iKey > index, filterIndex: index, ); + final columnWidths = _remapSource( this.columnWidths, index, @@ -379,6 +465,22 @@ extension TableMapOperation on Node { filterIndex: index, ); + final columnBoldAttributes = _remapSource( + this.columnBoldAttributes, + index, + increment: false, + comparator: (iKey, index) => iKey > index, + filterIndex: index, + ); + + final columnTextColors = _remapSource( + this.columnTextColors, + index, + increment: false, + comparator: (iKey, index) => iKey > index, + filterIndex: index, + ); + return attributes .mergeValues( SimpleTableBlockKeys.columnColors, @@ -391,6 +493,14 @@ extension TableMapOperation on Node { .mergeValues( SimpleTableBlockKeys.columnWidths, columnWidths, + ) + .mergeValues( + SimpleTableBlockKeys.columnBoldAttributes, + columnBoldAttributes, + ) + .mergeValues( + SimpleTableBlockKeys.columnTextColors, + columnTextColors, ); } catch (e) { Log.warn('Failed to map column deletion attributes: $e'); @@ -443,6 +553,22 @@ extension TableMapOperation on Node { filterIndex: index, ); + final rowBoldAttributes = _remapSource( + this.rowBoldAttributes, + index, + increment: false, + comparator: (iKey, index) => iKey > index, + filterIndex: index, + ); + + final rowTextColors = _remapSource( + this.rowTextColors, + index, + increment: false, + comparator: (iKey, index) => iKey > index, + filterIndex: index, + ); + return attributes .mergeValues( SimpleTableBlockKeys.rowColors, @@ -451,6 +577,14 @@ extension TableMapOperation on Node { .mergeValues( SimpleTableBlockKeys.rowAligns, rowAligns, + ) + .mergeValues( + SimpleTableBlockKeys.rowBoldAttributes, + rowBoldAttributes, + ) + .mergeValues( + SimpleTableBlockKeys.rowTextColors, + rowTextColors, ); } catch (e) { Log.warn('Failed to map row deletion attributes: $e'); @@ -531,6 +665,10 @@ extension TableMapOperation on Node { final duplicatedColumnColor = this.columnColors[fromIndex.toString()]; final duplicatedColumnAlign = this.columnAligns[fromIndex.toString()]; final duplicatedColumnWidth = this.columnWidths[fromIndex.toString()]; + final duplicatedColumnBoldAttribute = + this.columnBoldAttributes[fromIndex.toString()]; + final duplicatedColumnTextColor = + this.columnTextColors[fromIndex.toString()]; /// Case 1: fromIndex > toIndex /// Before: @@ -619,6 +757,34 @@ extension TableMapOperation on Node { filterIndex: fromIndex, ); + final columnBoldAttributes = _remapSource( + this.columnBoldAttributes, + fromIndex, + increment: fromIndex > toIndex, + comparator: (iKey, index) { + if (fromIndex > toIndex) { + return iKey < fromIndex && iKey >= toIndex; + } else { + return iKey > fromIndex && iKey <= toIndex; + } + }, + filterIndex: fromIndex, + ); + + final columnTextColors = _remapSource( + this.columnTextColors, + fromIndex, + increment: fromIndex > toIndex, + comparator: (iKey, index) { + if (fromIndex > toIndex) { + return iKey < fromIndex && iKey >= toIndex; + } else { + return iKey > fromIndex && iKey <= toIndex; + } + }, + filterIndex: fromIndex, + ); + return attributes .mergeValues( SimpleTableBlockKeys.columnColors, @@ -652,6 +818,28 @@ extension TableMapOperation on Node { ) : null, removeNullValue: true, + ) + .mergeValues( + SimpleTableBlockKeys.columnBoldAttributes, + columnBoldAttributes, + duplicatedEntry: duplicatedColumnBoldAttribute != null + ? MapEntry( + toIndex.toString(), + duplicatedColumnBoldAttribute, + ) + : null, + removeNullValue: true, + ) + .mergeValues( + SimpleTableBlockKeys.columnTextColors, + columnTextColors, + duplicatedEntry: duplicatedColumnTextColor != null + ? MapEntry( + toIndex.toString(), + duplicatedColumnTextColor, + ) + : null, + removeNullValue: true, ); } catch (e) { Log.warn('Failed to map column deletion attributes: $e'); @@ -667,6 +855,9 @@ extension TableMapOperation on Node { try { final duplicatedRowColor = this.rowColors[fromIndex.toString()]; final duplicatedRowAlign = this.rowAligns[fromIndex.toString()]; + final duplicatedRowBoldAttribute = + this.rowBoldAttributes[fromIndex.toString()]; + final duplicatedRowTextColor = this.rowTextColors[fromIndex.toString()]; /// Example: /// Case 1: fromIndex > toIndex @@ -742,6 +933,34 @@ extension TableMapOperation on Node { filterIndex: fromIndex, ); + final rowBoldAttributes = _remapSource( + this.rowBoldAttributes, + fromIndex, + increment: fromIndex > toIndex, + comparator: (iKey, index) { + if (fromIndex > toIndex) { + return iKey < fromIndex && iKey >= toIndex; + } else { + return iKey > fromIndex && iKey <= toIndex; + } + }, + filterIndex: fromIndex, + ); + + final rowTextColors = _remapSource( + this.rowTextColors, + fromIndex, + increment: fromIndex > toIndex, + comparator: (iKey, index) { + if (fromIndex > toIndex) { + return iKey < fromIndex && iKey >= toIndex; + } else { + return iKey > fromIndex && iKey <= toIndex; + } + }, + filterIndex: fromIndex, + ); + return attributes .mergeValues( SimpleTableBlockKeys.rowColors, @@ -764,6 +983,28 @@ extension TableMapOperation on Node { ) : null, removeNullValue: true, + ) + .mergeValues( + SimpleTableBlockKeys.rowBoldAttributes, + rowBoldAttributes, + duplicatedEntry: duplicatedRowBoldAttribute != null + ? MapEntry( + toIndex.toString(), + duplicatedRowBoldAttribute, + ) + : null, + removeNullValue: true, + ) + .mergeValues( + SimpleTableBlockKeys.rowTextColors, + rowTextColors, + duplicatedEntry: duplicatedRowTextColor != null + ? MapEntry( + toIndex.toString(), + duplicatedRowTextColor, + ) + : null, + removeNullValue: true, ); } catch (e) { Log.warn('Failed to map row reordering attributes: $e'); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_more_action_popup.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_more_action_popup.dart index d2e7340790..b7b5880b38 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_more_action_popup.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_more_action_popup.dart @@ -455,6 +455,21 @@ class _SimpleTableMoreActionItemState extends State { final columnIndex = node.columnIndex; final editorState = context.read(); editorState.insertColumnInTable(table, columnIndex); + + final cell = table.getTableCellNode( + rowIndex: 0, + columnIndex: columnIndex, + ); + if (cell == null) { + return; + } + + // update selection + editorState.selection = Selection.collapsed( + Position( + path: cell.path.child(0), + ), + ); } void _insertColumnRight() { @@ -466,6 +481,21 @@ class _SimpleTableMoreActionItemState extends State { final columnIndex = node.columnIndex; final editorState = context.read(); editorState.insertColumnInTable(table, columnIndex + 1); + + final cell = table.getTableCellNode( + rowIndex: 0, + columnIndex: columnIndex + 1, + ); + if (cell == null) { + return; + } + + // update selection + editorState.selection = Selection.collapsed( + Position( + path: cell.path.child(0), + ), + ); } void _insertRowAbove() { @@ -477,6 +507,18 @@ class _SimpleTableMoreActionItemState extends State { final rowIndex = node.rowIndex; final editorState = context.read(); editorState.insertRowInTable(table, rowIndex); + + final cell = table.getTableCellNode(rowIndex: rowIndex, columnIndex: 0); + if (cell == null) { + return; + } + + // update selection + editorState.selection = Selection.collapsed( + Position( + path: cell.path.child(0), + ), + ); } void _insertRowBelow() { @@ -488,6 +530,18 @@ class _SimpleTableMoreActionItemState extends State { final rowIndex = node.rowIndex; final editorState = context.read(); editorState.insertRowInTable(table, rowIndex + 1); + + final cell = table.getTableCellNode(rowIndex: rowIndex + 1, columnIndex: 0); + if (cell == null) { + return; + } + + // update selection + editorState.selection = Selection.collapsed( + Position( + path: cell.path.child(0), + ), + ); } void _deleteRow() { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart index de4d431a08..31377a93b7 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart @@ -166,9 +166,10 @@ class EditorStyleCustomizer { applyHeightToLastDescent: true, ), textSpanDecorator: customizeAttributeDecorator, - mobileDragHandleBallSize: const Size.square(12.0), magnifierSize: const Size(144, 96), textScaleFactor: textScaleFactor, + mobileDragHandleLeftExtend: 12.0, + mobileDragHandleWidthExtend: 24.0, ); } diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index b108a0c23a..8936824930 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -61,8 +61,8 @@ packages: dependency: "direct main" description: path: "." - ref: c68e5f6 - resolved-ref: c68e5f6c585205083e27e875b822656425b2853f + ref: cfb8b1b + resolved-ref: cfb8b1b6eb06f73a4fb297b6fd1d54b0ccec2922 url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git version: "4.0.0" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 9433511050..f40d61b4b0 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -174,7 +174,7 @@ dependency_overrides: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: "c68e5f6" + ref: "cfb8b1b" appflowy_editor_plugins: git: diff --git a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_delete_operation_test.dart b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_delete_operation_test.dart index c9c1be8379..cb28f955da 100644 --- a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_delete_operation_test.dart +++ b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_delete_operation_test.dart @@ -170,5 +170,67 @@ void main() { '0': TableAlign.center.key, }); }); + + test('delete a column with text color & bold style (1)', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 3, + columnCount: 4, + ); + // delete the column 1 + final tableCellNode = + tableNode.getTableCellNode(rowIndex: 0, columnIndex: 1); + await editorState.updateColumnTextColor( + tableCellNode: tableCellNode!, + color: '0xFF0000FF', + ); + await editorState.toggleColumnBoldAttribute( + tableCellNode: tableCellNode, + isBold: true, + ); + expect(tableNode.columnTextColors, { + '1': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '1': true, + }); + await editorState.deleteColumnInTable(tableNode, 0); + expect(tableNode.columnTextColors, { + '0': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '0': true, + }); + expect(tableNode.rowLength, 3); + expect(tableNode.columnLength, 3); + }); + + test('delete a column with text color & bold style (2)', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 3, + columnCount: 4, + ); + // delete the column 1 + final tableCellNode = + tableNode.getTableCellNode(rowIndex: 0, columnIndex: 1); + await editorState.updateColumnTextColor( + tableCellNode: tableCellNode!, + color: '0xFF0000FF', + ); + await editorState.toggleColumnBoldAttribute( + tableCellNode: tableCellNode, + isBold: true, + ); + expect(tableNode.columnTextColors, { + '1': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '1': true, + }); + await editorState.deleteColumnInTable(tableNode, 1); + expect(tableNode.columnTextColors, {}); + expect(tableNode.columnBoldAttributes, {}); + expect(tableNode.rowLength, 3); + expect(tableNode.columnLength, 3); + }); }); } diff --git a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_duplicate_operation_test.dart b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_duplicate_operation_test.dart index 123310538a..85a1c252c7 100644 --- a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_duplicate_operation_test.dart +++ b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_duplicate_operation_test.dart @@ -161,5 +161,69 @@ void main() { '1': TableAlign.center.key, }); }); + + test('duplicate a column with text color & bold style (1)', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 3, + columnCount: 4, + ); + // duplicate the column 1 + final tableCellNode = + tableNode.getTableCellNode(rowIndex: 0, columnIndex: 1); + await editorState.updateColumnTextColor( + tableCellNode: tableCellNode!, + color: '0xFF0000FF', + ); + await editorState.toggleColumnBoldAttribute( + tableCellNode: tableCellNode, + isBold: true, + ); + expect(tableNode.columnTextColors, { + '1': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '1': true, + }); + await editorState.duplicateColumnInTable(tableNode, 1); + expect(tableNode.columnTextColors, { + '1': '0xFF0000FF', + '2': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '1': true, + '2': true, + }); + }); + + test('duplicate a column with text color & bold style (2)', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 3, + columnCount: 4, + ); + // duplicate the column 1 + final tableCellNode = + tableNode.getTableCellNode(rowIndex: 0, columnIndex: 1); + await editorState.updateColumnTextColor( + tableCellNode: tableCellNode!, + color: '0xFF0000FF', + ); + await editorState.toggleColumnBoldAttribute( + tableCellNode: tableCellNode, + isBold: true, + ); + expect(tableNode.columnTextColors, { + '1': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '1': true, + }); + await editorState.duplicateColumnInTable(tableNode, 0); + expect(tableNode.columnTextColors, { + '2': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '2': true, + }); + }); }); } diff --git a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_insert_operation_test.dart b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_insert_operation_test.dart index c84615ac42..86c4236a03 100644 --- a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_insert_operation_test.dart +++ b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_insert_operation_test.dart @@ -190,5 +190,67 @@ void main() { '0': TableAlign.center.key, }); }); + + test('insert a column with text color & bold style (1)', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 2, + columnCount: 3, + ); + // insert the column at the first position + final tableCellNode = + tableNode.getTableCellNode(rowIndex: 0, columnIndex: 0); + await editorState.updateColumnTextColor( + tableCellNode: tableCellNode!, + color: '0xFF0000FF', + ); + await editorState.toggleColumnBoldAttribute( + tableCellNode: tableCellNode, + isBold: true, + ); + expect(tableNode.columnTextColors, { + '0': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '0': true, + }); + await editorState.insertColumnInTable(tableNode, 0); + expect(tableNode.columnTextColors, { + '1': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '1': true, + }); + }); + + test('insert a column with text color & bold style (2)', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 2, + columnCount: 3, + ); + // insert the column at the first position + final tableCellNode = + tableNode.getTableCellNode(rowIndex: 0, columnIndex: 0); + await editorState.updateColumnTextColor( + tableCellNode: tableCellNode!, + color: '0xFF0000FF', + ); + await editorState.toggleColumnBoldAttribute( + tableCellNode: tableCellNode, + isBold: true, + ); + expect(tableNode.columnTextColors, { + '0': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '0': true, + }); + await editorState.insertColumnInTable(tableNode, 1); + expect(tableNode.columnTextColors, { + '0': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '0': true, + }); + }); }); }