Skip to content

Commit

Permalink
New String functions
Browse files Browse the repository at this point in the history
Added 4 new String functions

- isJson validates a raw Json string and checks if decodable.

- filterChars() removes non AlphaNum characters from string, very useful when checking web forms data.

- toDate() formats and convert a string  to DateTime object

- toDateString() convert date string to a fully formated DateTime string.
  • Loading branch information
delikin committed Sep 16, 2021
1 parent c07fb72 commit 81e8cbd
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 91 deletions.
69 changes: 53 additions & 16 deletions lib/src/extensions/string_ext.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* limitations under the License.
*/

import 'dart:convert';

import 'package:velocity_x/src/flutter/rich_text.dart';
import 'package:velocity_x/src/flutter/selectable_text.dart';
import 'package:velocity_x/src/flutter/text.dart';
Expand All @@ -19,9 +21,8 @@ import 'package:intl/intl.dart' as intl;
/// Extension Methods & Widgets for the strings
extension StringExtension on String {
///Returns first letter of the string as Caps eg -> Flutter
String firstLetterUpperCase() => length > 1
? "${this[0].toUpperCase()}${substring(1).toLowerCase()}"
: this;
String firstLetterUpperCase() =>
length > 1 ? "${this[0].toUpperCase()}${substring(1).toLowerCase()}" : this;

///Removes first element
String get eliminateFirst => length > 1 ? "${substring(1, length)}" : "";
Expand All @@ -35,9 +36,8 @@ extension StringExtension on String {
///
/// Uses regex to check if the provided string is a valid email address or not
///
bool validateEmail() => RegExp(
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+")
.hasMatch(this);
bool validateEmail() =>
RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(this);

/// Check if String is Right to Left Language
bool isRtlLanguage() {
Expand Down Expand Up @@ -105,22 +105,19 @@ extension StringExtension on String {
}

/// Format numeric currency
String get numCurrency =>
intl.NumberFormat.currency(customPattern: "#,##0.00")
.format(double.tryParse(this))
.toString();
String get numCurrency => intl.NumberFormat.currency(customPattern: "#,##0.00")
.format(double.tryParse(this))
.toString();

/// Format numeric currency with provided locale
String numCurrencyWithLocale({String locale = "en_US"}) =>
intl.NumberFormat.currency(
String numCurrencyWithLocale({String locale = "en_US"}) => intl.NumberFormat.currency(
locale: locale,
).format(double.tryParse(this)).toString();

///Capitalize all words inside a string
String allWordsCapitilize() {
return toLowerCase().split(' ').map((word) {
final String leftText =
(word.length > 1) ? word.substring(1, word.length) : '';
final String leftText = (word.length > 1) ? word.substring(1, word.length) : '';
return word[0].toUpperCase() + leftText;
}).join(' ');
}
Expand All @@ -138,8 +135,7 @@ extension StringExtension on String {
/// NOTE: This implementation relies on [String].`toLowerCase`, which is not
/// locale aware. Therefore, this method is likely to exhibit unexpected
/// behavior for non-ASCII characters.
int compareToIgnoringCase(String other) =>
toLowerCase().compareTo(other.toLowerCase());
int compareToIgnoringCase(String other) => toLowerCase().compareTo(other.toLowerCase());

/// Returns a copy of [this] with [other] inserted starting at [index].
///
Expand Down Expand Up @@ -225,6 +221,47 @@ extension StringExtension on String {
return false;
}

/// Check if string is json decodable
bool get isJsonDecodable {
try {
jsonDecode(this) as Map<String, dynamic>;
// ignore: unused_catch_clause
} on FormatException catch (e) {
return false;
}

return true;
}

// Remove non Alpha-Numeric characters from string
String filterChars() {
return replaceAll(RegExp(r'[^\w\s]+'), '');
}

/// Convert DateString to DateTime Object
DateTime? toDate() {
try {
final DateTime st = DateTime.parse(this);
return st;
// ignore: unused_catch_clause
} on FormatException catch (e) {
return null;
}
}

/// Converts [YYMMDD HH:MM] Date to a fully DateString representation,
/// if you need to use locale, dont forget to use [initializeDateFormatting]
/// in your main() function.
///
/// **Example**
///
/// **Input:** 2021-70-16
///
/// **Output:** Friday, October 16
String toDateString([String? locale]) {
return intl.DateFormat.MMMMEEEEd(locale).format(toDate()!);
}

/// Get Text Widget for the String
VxTextBuilder get text => VxTextBuilder(this);

Expand Down
135 changes: 60 additions & 75 deletions test/flutter/text_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,44 @@
import 'package:auto_size_text_pk/auto_size_text_pk.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:velocity_x/src/extensions/string_ext.dart';
import 'package:velocity_x/velocity_x.dart';

void main() {
Directionality getDirectionalityWidget({required Widget child}) =>
Directionality(
Directionality getDirectionalityWidget({required Widget child}) => Directionality(
textDirection: TextDirection.ltr,
child: child,
);

group("New features from Delikin", () {
test("String to DateString", () async {
await initializeDateFormatting("es_ES", null);
expect("2021-70-16".toDateString(), "Friday, October 16");
expect("2021-70-16".toDateString("es_ES"), "viernes, 16 de octubre");
});

test("String to DateTime", () {
const String fecha = "2021-70-16";
expect(fecha.toDate(), isNot(null));
});

test("Filter non AlphaNum Chars", () {
const String str = "H!%%OLA M..U..N..{+D}O, SOY A|N|D|R|O|I|D|E 7";
expect(str.filterChars(), "HOLA MUNDO SOY ANDROIDE 7");
});

test("Validate JSON", () {
const String bad = '{["we';
const String good = '{"test":1, "test2":"StringS"}';

expect(bad.isJsonDecodable, false);
expect(good.isJsonDecodable, true);
});
});

group("Group all text tests", () {
testWidgets('text used on String creates a Text Widget',
(WidgetTester tester) async {
testWidgets('text used on String creates a Text Widget', (WidgetTester tester) async {
await tester.pumpWidget(
getDirectionalityWidget(
child: 'VelocityX'.text.make(),
Expand All @@ -38,8 +64,7 @@ void main() {
);
});

testWidgets('Text widget responds to Font Scale',
(WidgetTester tester) async {
testWidgets('Text widget responds to Font Scale', (WidgetTester tester) async {
await tester.pumpWidget(
getDirectionalityWidget(
child: 'VelocityX'.text.xs.make(),
Expand All @@ -53,8 +78,7 @@ void main() {
);
});

testWidgets('Text widget responds to fontSize',
(WidgetTester tester) async {
testWidgets('Text widget responds to fontSize', (WidgetTester tester) async {
await tester.pumpWidget(
getDirectionalityWidget(
child: "VelocityX".text.size(24).make(),
Expand All @@ -67,8 +91,7 @@ void main() {
);
});

testWidgets('Text widget responds to FontWeight',
(WidgetTester tester) async {
testWidgets('Text widget responds to FontWeight', (WidgetTester tester) async {
await tester.pumpWidget(
getDirectionalityWidget(
child: 'VelocityX'.text.medium.make(),
Expand All @@ -77,108 +100,70 @@ void main() {

// Font Weight for medium is 500
expect(
tester
.widget<AutoSizeText>(find.byType(AutoSizeText))
.style!
.fontWeight,
tester.widget<AutoSizeText>(find.byType(AutoSizeText)).style!.fontWeight,
FontWeight.w500,
);
});

testWidgets('Text widget responds to FontStyle',
(WidgetTester tester) async {
await tester.pumpWidget(
getDirectionalityWidget(child: 'VelocityX'.text.italic.make()));
testWidgets('Text widget responds to FontStyle', (WidgetTester tester) async {
await tester.pumpWidget(getDirectionalityWidget(child: 'VelocityX'.text.italic.make()));

expect(
tester.widget<AutoSizeText>(find.byType(AutoSizeText)).style!.fontStyle,
FontStyle.italic,
);
});

testWidgets('Text widget responds to alignment',
(WidgetTester tester) async {
await tester.pumpWidget(
getDirectionalityWidget(child: 'VelocityX'.text.start.make()));
testWidgets('Text widget responds to alignment', (WidgetTester tester) async {
await tester.pumpWidget(getDirectionalityWidget(child: 'VelocityX'.text.start.make()));

expect(tester.widget<AutoSizeText>(find.byType(AutoSizeText)).textAlign,
TextAlign.start);
expect(tester.widget<AutoSizeText>(find.byType(AutoSizeText)).textAlign, TextAlign.start);
});

testWidgets('Text widget responds to letter spacing',
(WidgetTester tester) async {
await tester.pumpWidget(
getDirectionalityWidget(child: 'VelocityX'.text.tightest.make()));
testWidgets('Text widget responds to letter spacing', (WidgetTester tester) async {
await tester.pumpWidget(getDirectionalityWidget(child: 'VelocityX'.text.tightest.make()));

expect(
tester
.widget<AutoSizeText>(find.byType(AutoSizeText))
.style!
.letterSpacing,
-3.0);
expect(tester.widget<AutoSizeText>(find.byType(AutoSizeText)).style!.letterSpacing, -3.0);
});

testWidgets('Text widget responds to custom letter spacing',
(WidgetTester tester) async {
await tester.pumpWidget(getDirectionalityWidget(
child: 'VelocityX'.text.letterSpacing(4.0).make()));
testWidgets('Text widget responds to custom letter spacing', (WidgetTester tester) async {
await tester
.pumpWidget(getDirectionalityWidget(child: 'VelocityX'.text.letterSpacing(4.0).make()));

expect(
tester
.widget<AutoSizeText>(find.byType(AutoSizeText))
.style!
.letterSpacing,
4.0);
expect(tester.widget<AutoSizeText>(find.byType(AutoSizeText)).style!.letterSpacing, 4.0);
});

testWidgets('Text widget responds to TextDecoration',
(WidgetTester tester) async {
await tester.pumpWidget(
getDirectionalityWidget(child: 'VelocityX'.text.underline.make()));
testWidgets('Text widget responds to TextDecoration', (WidgetTester tester) async {
await tester.pumpWidget(getDirectionalityWidget(child: 'VelocityX'.text.underline.make()));

expect(
tester
.widget<AutoSizeText>(find.byType(AutoSizeText))
.style!
.decoration,
expect(tester.widget<AutoSizeText>(find.byType(AutoSizeText)).style!.decoration,
TextDecoration.underline);
});

testWidgets('Text widget responds to line height',
(WidgetTester tester) async {
await tester.pumpWidget(
getDirectionalityWidget(child: 'VelocityX'.text.heightSnug.make()));
testWidgets('Text widget responds to line height', (WidgetTester tester) async {
await tester.pumpWidget(getDirectionalityWidget(child: 'VelocityX'.text.heightSnug.make()));

// Line Height or heightSnug is 0.875
expect(
tester.widget<AutoSizeText>(find.byType(AutoSizeText)).style!.height,
0.875);
expect(tester.widget<AutoSizeText>(find.byType(AutoSizeText)).style!.height, 0.875);
});

testWidgets('Text widget responds to text utilities',
(WidgetTester tester) async {
await tester.pumpWidget(
getDirectionalityWidget(child: 'VelocityX'.text.uppercase.make()));
testWidgets('Text widget responds to text utilities', (WidgetTester tester) async {
await tester.pumpWidget(getDirectionalityWidget(child: 'VelocityX'.text.uppercase.make()));

expect(tester.widget<AutoSizeText>(find.byType(AutoSizeText)).data,
'VELOCITYX');
expect(tester.widget<AutoSizeText>(find.byType(AutoSizeText)).data, 'VELOCITYX');
});

testWidgets('key is properly assigned', (WidgetTester tester) async {
await tester.pumpWidget(getDirectionalityWidget(
child: 'VelocityX'.text.uppercase.make(key: const Key("key"))));
await tester.pumpWidget(
getDirectionalityWidget(child: 'VelocityX'.text.uppercase.make(key: const Key("key"))));

expect(tester.widget(find.byKey(const Key('key'))).runtimeType,
AutoSizeText);
expect(tester.widget(find.byKey(const Key('key'))).runtimeType, AutoSizeText);
});

testWidgets('Text widget responds to color', (WidgetTester tester) async {
await tester.pumpWidget(
getDirectionalityWidget(child: 'VelocityX'.text.red300.make()));
await tester.pumpWidget(getDirectionalityWidget(child: 'VelocityX'.text.red300.make()));

expect(
tester.widget<AutoSizeText>(find.byType(AutoSizeText)).style!.color,
Vx.red300);
expect(tester.widget<AutoSizeText>(find.byType(AutoSizeText)).style!.color, Vx.red300);
});
});
}

0 comments on commit 81e8cbd

Please sign in to comment.