From d35bcf39b4ccd6aae242e695b6ba824183b63547 Mon Sep 17 00:00:00 2001 From: Josh Burton Date: Mon, 25 Nov 2024 22:59:24 +1300 Subject: [PATCH] fix: #270 allow deleting area codes (#271) * fix: #270 allow deleting area codes Formatting of the entered number is now skipped if the text input is within an area code, such as (416 and the closing parentheses is missing. * bump version and add to changelog --- CHANGELOG.md | 4 ++ example/pubspec.lock | 34 ++++++------- lib/src/phone_controller.dart | 8 +++ pubspec.yaml | 2 +- test/phone_form_field_test.dart | 86 ++++++++++++++++++++++++++++++++- 5 files changed, 114 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e28dcb63..0e3e323e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [10.0.2] + +- Fix fixes issue where area codes wrapped in parentheses cannot be deleted + ## [10.0.0] - Add missing countries and favorites params to CountrySelectorNavigator.page factory diff --git a/example/pubspec.lock b/example/pubspec.lock index beea6ba3..80b9fef0 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.18.0" diacritic: dependency: transitive description: @@ -140,18 +140,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.8" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -222,7 +222,7 @@ packages: path: ".." relative: true source: path - version: "10.0.0" + version: "10.0.2" phone_numbers_parser: dependency: transitive description: @@ -235,7 +235,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_span: dependency: transitive description: @@ -248,10 +248,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.11.1" stream_channel: dependency: transitive description: @@ -264,10 +264,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.2.0" term_glyph: dependency: transitive description: @@ -280,10 +280,10 @@ packages: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.2" typed_data: dependency: transitive description: @@ -328,10 +328,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "14.2.5" web: dependency: transitive description: @@ -349,5 +349,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.4.0 <4.0.0" + dart: ">=3.3.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/lib/src/phone_controller.dart b/lib/src/phone_controller.dart index f7d46593..6143dec2 100644 --- a/lib/src/phone_controller.dart +++ b/lib/src/phone_controller.dart @@ -37,8 +37,11 @@ class PhoneController extends ChangeNotifier { changeNationalNumber(String? text) { text = text ?? ''; + final oldFormattedText = _value.formatNsn(); var newFormattedText = text; + bool isDeleting = text.length < oldFormattedText.length; + // if starts with + then we parse the whole number final startsWithPlus = text.startsWith(RegExp('[${AllowedCharacters.plus}]')); @@ -51,6 +54,11 @@ class PhoneController extends ChangeNotifier { _value = phoneNumber; newFormattedText = _value.formatNsn(); } + } else if (isDeleting && + text.startsWith( + RegExp('^\\([${AllowedCharacters.digits}]+(?!.*\\))'))) { + // Handle case where the phone number contains an area code such as (416), and user has begun to delete it, i.e. the text input is now (416. + // We need to skip parsing/formatting here, else the parentheses will be added back } else { final phoneNumber = PhoneNumber.parse( text, diff --git a/pubspec.yaml b/pubspec.yaml index 0577c86e..51abd0c6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: phone_form_field description: Flutter phone input integrated with flutter internationalization -version: 10.0.0 +version: 10.0.2 homepage: https://github.com/cedvdb/phone_form_field environment: diff --git a/test/phone_form_field_test.dart b/test/phone_form_field_test.dart index 599b476a..5c04b71c 100644 --- a/test/phone_form_field_test.dart +++ b/test/phone_form_field_test.dart @@ -1,5 +1,6 @@ import 'package:circle_flags/circle_flags.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_country_selector/flutter_country_selector.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:phone_form_field/phone_form_field.dart'; @@ -112,6 +113,86 @@ void main() { expect(find.text('6 77 77 77 77'), findsOneWidget); }); + testWidgets('Can delete phone number', (tester) async { + final controller = PhoneController( + initialValue: PhoneNumber.parse('+64'), + ); + + await tester.pumpWidget(getWidget(controller: controller)); + await tester.pump(const Duration(seconds: 1)); + final phoneField = find.byType(PhoneFormField); + await tester.enterText(phoneField, '+64210000000'); + await tester.pump(const Duration(seconds: 1)); + expect(find.text('+ 64'), findsOneWidget); + expect(find.text('210 000 000'), findsOneWidget); + + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.pump(); + + expect(controller.value.nsn, equals('')); + }); + + testWidgets('Can delete phone number with area code in parentheses', + (tester) async { + final controller = PhoneController( + initialValue: PhoneNumber.parse('+1'), + ); + + await tester.pumpWidget(getWidget(controller: controller)); + await tester.pump(const Duration(seconds: 1)); + final phoneField = find.byType(PhoneFormField); + await tester.enterText(phoneField, '+14165555555'); + await tester.pump(const Duration(seconds: 1)); + expect(find.text('+ 1'), findsOneWidget); + expect(find.text('(416) 555-5555'), findsOneWidget); + + // delete all digits up to the area code (416) + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + + // attempt to delete area code + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + + expect(find.text('(416'), findsOneWidget); + + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + await tester.sendKeyEvent(LogicalKeyboardKey.backspace); + + expect(find.text('(416)'), findsNothing); + expect(controller.value.nsn, equals('')); + }); + + testWidgets('Can enter phone number with area code', (tester) async { + final controller = PhoneController( + initialValue: PhoneNumber.parse('+1'), + ); + + await tester.pumpWidget(getWidget(controller: controller)); + await tester.pump(const Duration(seconds: 1)); + final phoneField = find.byType(PhoneFormField); + + await tester.enterText(phoneField, '(416'); + + await tester.pump(const Duration(seconds: 1)); + + expect(find.text('+ 1'), findsOneWidget); + expect(find.text('(416)'), findsOneWidget); + }); + testWidgets('Should show dial code when showDialCode is true', (tester) async { PhoneNumber phoneNumber = PhoneNumber.parse('+33'); @@ -250,10 +331,11 @@ void main() { ); }); - testWidgets('Should call onChange when countryCode updated', (tester) async { + testWidgets('Should call onChange when countryCode updated', + (tester) async { bool changed = false; PhoneNumber? phoneNumber = - PhoneNumber.parse('', destinationCountry: IsoCode.FR); + PhoneNumber.parse('', destinationCountry: IsoCode.FR); void onChanged(PhoneNumber? p) { changed = true; phoneNumber = p;