Skip to content

Commit

Permalink
Create validator class (#45)
Browse files Browse the repository at this point in the history
* rewrite the validator class

* add isValid State to the validator

* update Validator documentation

* fix codestyle errors
  • Loading branch information
yveskalume authored Jun 4, 2024
1 parent 6dd7f69 commit d2df31f
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 112 deletions.
29 changes: 17 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ This is what it looks like :
@Composable
fun MyScreen() {

val emailField = remember { EmailValidable() }

TextField(
val emailField = remember { EmailValidable() }

// pass all fields to the withValidable method
val validator = rememberValidator(emailField)


TextField(
value = emailField.value,
onValueChange = { emailField.value = it }, // update the text
isError = emailField.hasError(), // check if the field is not valid
Expand All @@ -34,15 +38,16 @@ fun MyScreen() {

}

Button(onClick = {
// pass all fields to the withValidable method
withValidable(emailField) {

// will be executed if all fields are valid
Toast.makeText(context,"All fields are valid",Toast.LENGTH_SHORT).show()

}
}) {
Button(
// a state to check if all fields are valid, without submitting the form
enabled = validator.isValid,
onClick = {
validator.validate {
// will be executed if all fields are valid
Toast.makeText(context, "All fields are valid", Toast.LENGTH_SHORT).show()
}
}
) {
Text(text = "Submit")
}
}
Expand Down
71 changes: 38 additions & 33 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,44 @@ Validating text fields when using jetpack compose can sometimes be challenging a
Validable is an extensible library that allows you to validate your text fields in a simpler way while having a reusable code.

```kotlin
@Composable
fun MyScreen() {

val emailField = remember { EmailValidable() }

TextField(
value = emailField.value,
onValueChange = { emailField.value = it }, // update the text
isError = emailField.hasError(), // check if the field is not valid
)

AnimatedVisibility(visible = emailField.hasError()) {

Text(
text = emailField.errorMessage ?: "",
modifier = Modifier.fillMaxWidth(),
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.error)
)

}

Button(onClick = {
// pass all fields to the withValidable method
withValidable(emailField) {

// will be executed if all fields are valid
Toast.makeText(context,"All fields are valid",Toast.LENGTH_SHORT).show()

}
}) {
Text(text = "Submit")
}
}
@Composable
fun MyScreen() {

val emailField = remember { EmailValidable() }

// pass all fields to the withValidable method
val validator = rememberValidator(emailField)


TextField(
value = emailField.value,
onValueChange = { emailField.value = it }, // update the text
isError = emailField.hasError(), // check if the field is not valid
)

AnimatedVisibility(visible = emailField.hasError()) {

Text(
text = emailField.errorMessage ?: "",
modifier = Modifier.fillMaxWidth(),
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.error)
)

}

Button(
// a state to check if all fields are valid, without submitting the form
enabled = validator.isValid,
onClick = {
validator.validate {
// will be executed if all fields are valid
Toast.makeText(context, "All fields are valid", Toast.LENGTH_SHORT).show()
}
}
) {
Text(text = "Submit")
}
}
```

## Installation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import tech.devscast.validable.EmailValidable
import tech.devscast.validable.GreaterThanValidable
import tech.devscast.validable.NotEmptyValidable
import tech.devscast.validable.UrlValidable
import tech.devscast.validable.withValidable
import tech.devscast.validable.core.rememberValidator

@ExperimentalAnimationApi
@Composable
Expand All @@ -44,14 +44,14 @@ fun InputScreen() {

val cardField = remember { CardSchemeValidable(CardScheme.MasterCard) }

val urlField = remember {
UrlValidable()
}
val urlField = remember { UrlValidable() }

val ageTextField = remember {
GreaterThanValidable(18, "Age must be greater than 18")
}

val validator = rememberValidator(emailField, nameField, cardField, urlField, ageTextField)

Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
Expand Down Expand Up @@ -141,19 +141,25 @@ fun InputScreen() {
Button(
modifier = Modifier.fillMaxWidth(),
onClick = {
withValidable(
emailField,
nameField,
cardField,
urlField,
ageTextField
) {
validator.validate {
Toast.makeText(context, "All fields are valid", Toast.LENGTH_SHORT).show()
}
}
) {
Text(text = "Continue", style = MaterialTheme.typography.labelLarge)
}

Spacer(modifier = Modifier.height(16.dp))

Button(
enabled = validator.isValid,
modifier = Modifier.fillMaxWidth(),
onClick = {
Toast.makeText(context, "All fields are valid", Toast.LENGTH_SHORT).show()
}
) {
Text(text = "Enabled only when is valid", style = MaterialTheme.typography.labelLarge)
}
Spacer(modifier = Modifier.height(24.dp))
}
}
Expand Down
12 changes: 7 additions & 5 deletions validable/api/validable.api
Original file line number Diff line number Diff line change
Expand Up @@ -242,12 +242,14 @@ public final class tech/devscast/validable/UrlValidable : tech/devscast/validabl
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
}

public final class tech/devscast/validable/WithValidableKt {
public static final fun withValidable ([Ltech/devscast/validable/BaseValidable;Lkotlin/jvm/functions/Function0;)V
public final class tech/devscast/validable/core/Validator {
public static final field $stable I
public fun <init> ([Ltech/devscast/validable/BaseValidable;)V
public final fun isValid (Landroidx/compose/runtime/Composer;I)Z
public final fun validate (Lkotlin/jvm/functions/Function0;)V
}

public final class tech/devscast/validable/delegates/ValidablesDelegatesKt {
public static final fun validableEmail (Landroidx/compose/runtime/Composer;I)Lkotlin/properties/ReadOnlyProperty;
public static final fun validableNotEmpty (Landroidx/compose/runtime/Composer;I)Lkotlin/properties/ReadOnlyProperty;
public final class tech/devscast/validable/core/ValidatorKt {
public static final fun rememberValidator ([Ltech/devscast/validable/BaseValidable;Landroidx/compose/runtime/Composer;I)Ltech/devscast/validable/core/Validator;
}

20 changes: 0 additions & 20 deletions validable/src/main/java/tech/devscast/validable/WithValidable.kt

This file was deleted.

53 changes: 53 additions & 0 deletions validable/src/main/java/tech/devscast/validable/core/Validator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package tech.devscast.validable.core

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import tech.devscast.validable.BaseValidable

/**
* Creates a new instance of a [Validator]
*
* @param fields
* all fields to validate
*/
@Composable
fun rememberValidator(
vararg fields: BaseValidable,
) = remember {
Validator(fields = fields)
}

class Validator(private vararg val fields: BaseValidable) {

/**
* Represents the validity of the fields.
* Can trigger re-composition.
*/
val isValid: Boolean
@Composable
get() {
val isValidValue by produceState(initialValue = false, fields.map { it.isValid }) {
this.value = fields.none { it.isValid.not() }
}
return isValidValue
}

/**
* Executes the provided action only if all tracked fields are currently valid.
*
* @param action
* The action to be executed if all fields are valid.
*/
fun validate(action: () -> Unit) {
val isThereAnyError = fields.map {
it.enableShowErrors()
it.hasError()
}.contains(true)

if (isThereAnyError.not()) {
action()
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package tech.devscast.validable.core

import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import tech.devscast.validable.EmailValidable
import tech.devscast.validable.EqualToValidable

class ValidatorTest {

@Test
fun `test validator when input are not valid`(){
val validable1 = EmailValidable()
validable1.value = "example"
val validable2 = EqualToValidable("Hello world")
validable2.value = "Hello"
val validator = Validator(validable1,validable2)
var isValid = false
validator.validate {
isValid = true
}
assertFalse("Should be false",isValid)
}

@Test

fun `test validator when input are valid`(){
val validable1 = EmailValidable()
validable1.value = "[email protected]"
val validable2 = EqualToValidable("Hello world")
validable2.value = "Hello world"
val validator = Validator(validable1,validable2)
var isValid = false
validator.validate {
isValid = true
}
assertTrue("Should be true",isValid)
}
}

0 comments on commit d2df31f

Please sign in to comment.