Skip to content

Commit

Permalink
Merge branch 'main' into fix/pure-fix-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
M1chlCZ authored Oct 7, 2024
2 parents e7e943d + ee93e26 commit e073d39
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 24 deletions.
1 change: 1 addition & 0 deletions glade_forms/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## DEV:
- **[Add]**: Add `IntInput` as a specialized variant of GladeInput<int> which has additional, int related, validations such as `isBetween`, `isMin`, `isMax`
- **[Add]**: Support skipping particular validation with `shouldValidate` callback.
- **[Breaking]**: The `resetToPure` method on both GladeInput and GladeModel has been renamed to `setAsNewPure`. This change better reflects the method's behavior of setting a new pure state rather than resetting to the original state.
- **[Add]**: New `resetToPure` method added to both GladeInput and GladeModel. This method truly resets the input(s) to their initial value(s) and marks them as pure.
Expand Down
15 changes: 15 additions & 0 deletions glade_forms/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,21 @@ StringInput is specialized variant of GladeInput<String> which has additional, s

Moreover `StringInput` by default uses TextEditingController under the hood.

#### IntInput

IntInput is specialized variant of GladeInput<int> which has additional, int related, validations such as `isBetween`, `isMin`, `isMax` and more.

- `isBetween` - checks if value is between min and max value. It has optional parameter `inclusiveInterval` which defines if min and max values are included in range.
- `isMin` - checks if value is greater or equal to min value.
- `isMax` - checks if value is less or equal to max value.

Moreover `IntInput` by default uses TextEditingController under the hood.

```dart
final validator = (IntValidator()..isMax(max: 10)).build();
final result = validator.validate(5); // valid
```

### Validation

Validation is defined through part methods on ValidatorFactory such as `notNull()`, `satisfy()` and other parts.
Expand Down
12 changes: 10 additions & 2 deletions glade_forms/example/lib/example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@ import 'package:glade_forms/glade_forms.dart';
// ! When updating dont forget to update README.md quickstart as well
class _Model extends GladeModel {
late StringInput name;
late GladeInput<int> age;
late IntInput age;
late StringInput email;
late IntInput income;

@override
List<GladeInput<Object?>> get inputs => [name, age, email];
List<GladeInput<Object?>> get inputs => [name, age, email, income];

@override
void initialize() {
name = GladeInput.stringInput(inputKey: 'name');
age = GladeInput.intInput(value: 0, inputKey: 'age');
email = GladeInput.stringInput(validator: (validator) => (validator..isEmail()).build(), inputKey: 'email');
income = GladeInput.intInput(
value: 10000, validator: (validator) => (validator..isMin(min: 1000)).build(), inputKey: 'income');

super.initialize();
}
Expand Down Expand Up @@ -48,6 +51,11 @@ class Example extends StatelessWidget {
validator: model.email.textFormFieldInputValidator,
decoration: const InputDecoration(labelText: 'Email'),
),
TextFormField(
controller: model.income.controller,
validator: model.income.textFormFieldInputValidator,
decoration: const InputDecoration(labelText: 'income'),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: model.isValid ? () {} : null,
Expand Down
1 change: 1 addition & 0 deletions glade_forms/lib/src/core/glade_error_keys.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ class GladeErrorKeys {
static const String stringExactLength = 'string-exact-length';
static const String valueIsNull = 'value-is-null';
static const String valueIsEmpty = 'value-is-empty';
static const String intCompareError = 'int-compare-error';
}
45 changes: 25 additions & 20 deletions glade_forms/lib/src/core/glade_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ import 'package:meta/meta.dart';
typedef ValueComparator<T> = bool Function(T? initial, T? value);
typedef ValidatorFactory<T> = ValidatorInstance<T> Function(GladeValidator<T> v);
typedef StringValidatorFactory = ValidatorInstance<String> Function(StringValidator validator);
typedef IntValidatorFactory = ValidatorInstance<int> Function(IntValidator validator);
typedef OnChange<T> = void Function(ChangesInfo<T> info);
typedef OnDependencyChange = void Function(List<String> updateInputKeys);
typedef ValueTransform<T> = T Function(T input);

typedef StringInput = GladeInput<String>;
typedef IntInput = GladeInput<int>;

class GladeInput<T> {
/// Compares initial and current value.
Expand Down Expand Up @@ -331,7 +333,7 @@ class GladeInput<T> {
required int value,
String? inputKey,
int? initialValue,
ValidatorFactory<int>? validator,
IntValidatorFactory? validator,
bool pure = true,
ErrorTranslator<int>? translateError,
DefaultTranslations? defaultTranslations,
Expand All @@ -343,25 +345,28 @@ class GladeInput<T> {
bool useTextEditingController = false,
ValueTransform<int>? valueTransform,
bool trackUnchanged = true,
}) =>
GladeInput.create(
value: value,
initialValue: initialValue ?? value,
validator: validator,
pure: pure,
translateError: translateError,
defaultTranslations: defaultTranslations,
valueComparator: valueComparator,
inputKey: inputKey,
dependencies: dependencies,
valueConverter: GladeTypeConverters.intConverter,
onChange: onChange,
onDependencyChange: onDependencyChange,
textEditingController: textEditingController,
useTextEditingController: useTextEditingController,
valueTransform: valueTransform,
trackUnchanged: trackUnchanged,
);
}) {
final validatorInstance = validator?.call(IntValidator()) ?? IntValidator().build();

return GladeInput._(
value: value,
initialValue: initialValue ?? value,
validatorInstance: validatorInstance,
isPure: pure,
translateError: translateError,
defaultTranslations: defaultTranslations,
valueComparator: valueComparator,
inputKey: inputKey,
dependenciesFactory: dependencies,
stringTovalueConverter: GladeTypeConverters.intConverter,
onChange: onChange,
onDependencyChange: onDependencyChange,
textEditingController: textEditingController,
useTextEditingController: useTextEditingController,
valueTransform: valueTransform,
trackUnchanged: trackUnchanged,
);
}

static GladeInput<bool> boolInput({
required bool value,
Expand Down
51 changes: 51 additions & 0 deletions glade_forms/lib/src/validator/int_validator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'package:glade_forms/src/src.dart';

class IntValidator extends GladeValidator<int> {
IntValidator();

/// Compares given value with [min] and [max] values. With [inclusiveInterval] set to true(default), the comparison is inclusive.
void isBetween({
required int min,
required int max,
OnValidateError<int>? devError,
Object? key,
ShouldValidateCallback<int>? shouldValidate,
bool inclusiveInterval = true,
}) =>
satisfy(
(value) => inclusiveInterval ? value >= min && value <= max : value > min && value < max,
devError: devError ??
(value) =>
'Value ${value ?? 'NULL'} (inclusiveInterval: $inclusiveInterval) is not in between $min and $max.',
key: key ?? GladeErrorKeys.intCompareError,
shouldValidate: shouldValidate,
);

/// Compares given value with [min] value.
void isMin({
required int min,
OnValidateError<int>? devError,
Object? key,
ShouldValidateCallback<int>? shouldValidate,
}) =>
satisfy(
(value) => value >= min,
devError: devError ?? (value) => 'Value ${value ?? 'NULL'} is less than $min.',
key: key ?? GladeErrorKeys.intCompareError,
shouldValidate: shouldValidate,
);

/// Compares given value with [max] value.
void isMax({
required int max,
OnValidateError<int>? devError,
Object? key,
ShouldValidateCallback<int>? shouldValidate,
}) =>
satisfy(
(value) => value <= max,
devError: devError ?? (value) => 'Value ${value ?? 'NULL'} is bigger than $max.',
key: key ?? GladeErrorKeys.intCompareError,
shouldValidate: shouldValidate,
);
}
1 change: 1 addition & 0 deletions glade_forms/lib/src/validator/validator.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export 'glade_validator.dart';
export 'int_validator.dart';
export 'part/part.dart';
export 'regex_patterns.dart';
export 'string_validator.dart';
Expand Down
102 changes: 102 additions & 0 deletions glade_forms/test/int_validator_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// ignore_for_file: avoid-unsafe-collection-methods

import 'package:glade_forms/src/validator/int_validator.dart';
import 'package:test/test.dart';

void main() {
group('inBetween', () {
test('success', () {
final validator = (IntValidator()..isBetween(min: 5, max: 10)).build();

final result = validator.validate(6);

expect(result.isValid, isTrue);
expect(result.isInvalid, isFalse);
});

test('success inclusive max', () {
final validator = (IntValidator()..isBetween(min: 5, max: 10)).build();

final result = validator.validate(10);

expect(result.isValid, isTrue);
expect(result.isInvalid, isFalse);
});

test('success inclusive min', () {
final validator = (IntValidator()..isBetween(min: 5, max: 10)).build();

final result = validator.validate(5);

expect(result.isValid, isTrue);
expect(result.isInvalid, isFalse);
});

test('fails non-inclusive max', () {
final validator = (IntValidator()..isBetween(min: 5, max: 10, inclusiveInterval: false)).build();

final result = validator.validate(10);

expect(result.isValid, isFalse);
expect(result.isInvalid, isTrue);
});

test('fails non-inclusive min', () {
final validator = (IntValidator()..isBetween(min: 5, max: 10, inclusiveInterval: false)).build();

final result = validator.validate(5);

expect(result.isValid, isFalse);
expect(result.isInvalid, isTrue);
});

test('fails', () {
final validator = (IntValidator()..isBetween(min: 5, max: 10)).build();

final result = validator.validate(1);

expect(result.isValid, isFalse);
expect(result.isInvalid, isTrue);
});
});

group('isMax', () {
test('success', () {
final validator = (IntValidator()..isMax(max: 10)).build();

final result = validator.validate(5);

expect(result.isValid, isTrue);
expect(result.isInvalid, isFalse);
});

test('fails', () {
final validator = (IntValidator()..isMax(max: 10)).build();

final result = validator.validate(25);

expect(result.isValid, isFalse);
expect(result.isInvalid, isTrue);
});
});

group('isMin', () {
test('success', () {
final validator = (IntValidator()..isMin(min: 1)).build();

final result = validator.validate(5);

expect(result.isValid, isTrue);
expect(result.isInvalid, isFalse);
});

test('fails', () {
final validator = (IntValidator()..isMin(min: 10)).build();

final result = validator.validate(5);

expect(result.isValid, isFalse);
expect(result.isInvalid, isTrue);
});
});
}
15 changes: 13 additions & 2 deletions storybook/lib/usecases/quickstart_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import 'package:glade_forms_storybook/shared/usecase_container.dart';

class _Model extends GladeModel {
late StringInput name;
late GladeInput<int> age;
late IntInput age;
late StringInput email;
late IntInput income;

@override
List<GladeInput<Object?>> get inputs => [name, age, email];
List<GladeInput<Object?>> get inputs => [name, age, email, income];

_Model();

Expand All @@ -17,6 +18,11 @@ class _Model extends GladeModel {
name = GladeInput.stringInput(inputKey: 'name');
age = GladeInput.intInput(value: 0, inputKey: 'age', useTextEditingController: true);
email = GladeInput.stringInput(validator: (validator) => (validator..isEmail()).build(), inputKey: 'email');
income = GladeInput.intInput(
value: 10000,
validator: (validator) => (validator..isMin(min: 1000)).build(),
inputKey: 'income',
);

super.initialize();
}
Expand Down Expand Up @@ -53,6 +59,11 @@ class QuickStartExample extends StatelessWidget {
validator: model.email.textFormFieldInputValidator,
decoration: const InputDecoration(labelText: 'Email'),
),
TextFormField(
controller: model.income.controller,
validator: model.income.textFormFieldInputValidator,
decoration: const InputDecoration(labelText: 'Income'),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: model.isValid
Expand Down

0 comments on commit e073d39

Please sign in to comment.