Skip to content

Commit

Permalink
fix: support character deletion while holding down the Shift key. (#659)
Browse files Browse the repository at this point in the history
* fix: support deletion of character using [shift+backspace] command shortcut

* refactor: remove unnecessary command handler and unit test
  • Loading branch information
Jayaprakash-dev authored Jan 10, 2024
1 parent 26c24ec commit a8a88c4
Show file tree
Hide file tree
Showing 2 changed files with 240 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import 'package:flutter/material.dart';
///
final CommandShortcutEvent backspaceCommand = CommandShortcutEvent(
key: 'backspace',
command: 'backspace',
command: 'backspace, shift+backspace',
handler: _backspaceCommandHandler,
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';

import '../../../infra/testable_editor.dart';

// single | means the cursor
// double | means the selection
void main() async {
group('deletion of a character while holding down shift key - widget test',
() {
const text = 'Welcome to AppFlowy Editor 🔥!';
const List<LogicalKeyboardKey> keys = [
LogicalKeyboardKey.shift,
LogicalKeyboardKey.backspace,
];
// Before
// |Welcome| to AppFlowy Editor 🔥!
// After
// | to AppFlowy Editor 🔥!
testWidgets('Delete the collapsed selection', (tester) async {
final editor = tester.editor
..addParagraph(
initialText: text,
);
await editor.startTesting();

// Welcome| to AppFlowy Editor 🔥!
const welcome = 'Welcome';
final selection = Selection.single(
path: [0],
startOffset: 0,
endOffset: welcome.length,
);
await editor.updateSelection(selection);

for (final LogicalKeyboardKey key in keys) {
await simulateKeyDownEvent(key);
await tester.pumpAndSettle();
}

// the first node should be deleted.
expect(
editor.nodeAtPath([0])?.delta?.toPlainText(),
text.substring(welcome.length),
);

await editor.dispose();
});

// Before
// # Welcome to |AppFlowy Editor 🔥!
// * Welcome to |AppFlowy Editor 🔥!
// * Welcome to AppFlowy Editor 🔥!
// After
// # Welcome to AppFlowy Editor 🔥!
// * Welcome to AppFlowy Editor 🔥!
testWidgets(
'Delete the collapsed selection and the first node can\'t have children',
(tester) async {
final delta = Delta()..insert(text);
final editor = tester.editor
..addNode(headingNode(level: 1, delta: delta))
..addNode(
bulletedListNode(
delta: delta,
children: [bulletedListNode(delta: delta)],
),
);

await editor.startTesting();

const welcome = 'Welcome to ';
final selection = Selection(
start: Position(
path: [0],
offset: welcome.length,
),
end: Position(
path: [1],
offset: welcome.length,
),
);
await editor.updateSelection(selection);

for (final LogicalKeyboardKey key in keys) {
await simulateKeyDownEvent(key);
await tester.pumpAndSettle();
}

expect(
editor.nodeAtPath([0])?.delta?.toPlainText(),
text,
);

final bulletedNode = editor.nodeAtPath([1])!;
expect(bulletedNode.type, BulletedListBlockKeys.type);
expect(bulletedNode.delta!.toPlainText(), text);

await editor.dispose();
});

// Before
// * Welcome to |AppFlowy Editor 🔥!
// * Welcome to |AppFlowy Editor 🔥!
// * Welcome to AppFlowy Editor 🔥!
// After
// # Welcome to AppFlowy Editor 🔥!
// * Welcome to AppFlowy Editor 🔥!
testWidgets(
'Delete the collapsed selection and the first node can have children',
(tester) async {
final delta = Delta()..insert(text);
final editor = tester.editor
..addNode(bulletedListNode(delta: delta))
..addNode(
bulletedListNode(
delta: delta,
children: [bulletedListNode(delta: delta)],
),
);

await editor.startTesting();

const welcome = 'Welcome to ';
final selection = Selection(
start: Position(
path: [0],
offset: welcome.length,
),
end: Position(
path: [1],
offset: welcome.length,
),
);
await editor.updateSelection(selection);

for (final LogicalKeyboardKey key in keys) {
await simulateKeyDownEvent(key);
await tester.pumpAndSettle();
}

expect(
editor.nodeAtPath([0])?.delta?.toPlainText(),
text,
);

final bulletedNode = editor.nodeAtPath([0, 0])!;
expect(bulletedNode.type, BulletedListBlockKeys.type);
expect(bulletedNode.delta!.toPlainText(), text);

await editor.dispose();
});

// Before
// Welcome to AppFlowy Editor 🔥!
// |---|
// Welcome to AppFlowy Editor 🔥!
// After
// Welcome to AppFlowy Editor 🔥!
// |Welcome to AppFlowy Editor 🔥!
testWidgets('Delete the non-text node, such as divider', (tester) async {
final editor = tester.editor
..addParagraph(initialText: text)
..addNode(dividerNode())
..addParagraph(initialText: text);

await editor.startTesting();

final selection = Selection.single(
path: [1],
startOffset: 0,
endOffset: 1,
);
await editor.updateSelection(selection);

for (final LogicalKeyboardKey key in keys) {
await simulateKeyDownEvent(key);
await tester.pumpAndSettle();
}

expect(
editor.nodeAtPath([1])?.delta?.toPlainText(),
text,
);
expect(
editor.selection,
Selection.collapsed(Position(path: [1])),
);

await editor.dispose();
});

testWidgets("clear text but keep the old direction", (tester) async {
final editor = tester.editor
..addNode(
paragraphNode(
text: 'Hello',
textDirection: blockComponentTextDirectionLTR,
),
)
..addNode(
paragraphNode(
text: 'س',
textDirection: blockComponentTextDirectionAuto,
),
);
await editor.startTesting();

Node node = editor.nodeAtPath([1])!;
expect(
node.selectable?.textDirection().name,
blockComponentTextDirectionRTL,
);

final selection = Selection.collapsed(
Position(path: [1], offset: 1),
);
await editor.updateSelection(selection);

for (final LogicalKeyboardKey key in keys) {
await simulateKeyDownEvent(key);
await tester.pumpAndSettle();
}

node = editor.nodeAtPath([1])!;
expect(
node.delta?.toPlainText().isEmpty,
true,
);
expect(
node.selectable?.textDirection().name,
blockComponentTextDirectionRTL,
);

await editor.dispose();
});
});
}

0 comments on commit a8a88c4

Please sign in to comment.