diff --git a/catalog/src/main/java/com/telefonica/mistica/catalog/ui/compose/components/Inputs.kt b/catalog/src/main/java/com/telefonica/mistica/catalog/ui/compose/components/Inputs.kt index 43600137d..28a40953f 100644 --- a/catalog/src/main/java/com/telefonica/mistica/catalog/ui/compose/components/Inputs.kt +++ b/catalog/src/main/java/com/telefonica/mistica/catalog/ui/compose/components/Inputs.kt @@ -72,6 +72,8 @@ fun Inputs() { TextAreaInputSample() Title("Check Box input") CheckBoxInputSample() + DisableCheckBoxInputSample(isChecked = true) + DisableCheckBoxInputSample(isChecked = false) Title("Dropdown") DropDownSample() Title("Disable Dropdown") @@ -385,6 +387,22 @@ fun CheckBoxInputSample() { ) } +@Composable +fun DisableCheckBoxInputSample( + isChecked: Boolean, +) { + CheckBoxInput( + checked = isChecked, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp, start = 16.dp, end = 16.dp), + text = "This is a disabled checkbox", + enabled = false, + errorText = null, + onCheckedChange = { }, + ) +} + @Composable private fun DropDownSample(enabled: Boolean = true) { val items = remember { diff --git a/catalog/src/main/res/layout/screen_inputs_catalog.xml b/catalog/src/main/res/layout/screen_inputs_catalog.xml index 43f095f2a..1874c49a9 100644 --- a/catalog/src/main/res/layout/screen_inputs_catalog.xml +++ b/catalog/src/main/res/layout/screen_inputs_catalog.xml @@ -311,6 +311,14 @@ app:inputCheckText="This is a disabled CheckBox" app:inputEnabled="false"/> + + Unit = {}, ) { - Column(modifier = modifier) { + Column( + modifier = modifier.alpha(enabled) + ) { Row { CompositionLocalProvider(LocalMinimumTouchTargetEnforcement provides false) { Checkbox( checked = checked, onCheckedChange = onCheckedChange, enabled = enabled, + colors = CheckboxDefaults.colors( + checkedColor = MisticaTheme.colors.controlActivated, + uncheckedColor = MisticaTheme.colors.control, + disabledColor = if (checked) { + MisticaTheme.colors.controlActivated + } else { + MisticaTheme.colors.control + } + ) ) } Spacer(modifier = Modifier.width(8.dp)) diff --git a/library/src/main/java/com/telefonica/mistica/input/CheckBoxInput.kt b/library/src/main/java/com/telefonica/mistica/input/CheckBoxInput.kt index d78e51ff9..12a70ae55 100644 --- a/library/src/main/java/com/telefonica/mistica/input/CheckBoxInput.kt +++ b/library/src/main/java/com/telefonica/mistica/input/CheckBoxInput.kt @@ -1,6 +1,7 @@ package com.telefonica.mistica.input import android.content.Context +import android.content.res.ColorStateList import android.graphics.drawable.Drawable import android.os.Build import android.text.method.MovementMethod @@ -12,6 +13,7 @@ import androidx.appcompat.widget.AppCompatCheckBox import androidx.databinding.* import com.google.android.material.textfield.TextInputLayout import com.telefonica.mistica.R +import com.telefonica.mistica.util.getThemeColor @BindingMethods( BindingMethod( @@ -74,6 +76,7 @@ class CheckBoxInput @JvmOverloads constructor( setChecked(initialInputChecked) setText(initialInputText) configureErrorResetOnCheckChange() + setButtonTint() return findViewById(R.id.text_input_layout) } @@ -85,6 +88,18 @@ class CheckBoxInput @JvmOverloads constructor( } } + private fun setButtonTint() { + val states = arrayOf( + intArrayOf(android.R.attr.state_checked), + intArrayOf(-android.R.attr.state_checked), + ) + val colors = intArrayOf( + context.getThemeColor(R.attr.colorControlActive), + context.getThemeColor(R.attr.colorControl), + ) + checkBox.buttonTintList = ColorStateList(states, colors) + } + fun setChecked(checked: Boolean) { checkBox.isChecked = checked } diff --git a/library/src/test/java/com/telefonica/mistica/button/ButtonTest.kt b/library/src/test/java/com/telefonica/mistica/button/ButtonTest.kt index e02ae44ef..7545424c5 100644 --- a/library/src/test/java/com/telefonica/mistica/button/ButtonTest.kt +++ b/library/src/test/java/com/telefonica/mistica/button/ButtonTest.kt @@ -20,6 +20,7 @@ import com.telefonica.mistica.compose.theme.brand.TelefonicaBrand import com.telefonica.mistica.compose.theme.brand.TuBrand import com.telefonica.mistica.compose.theme.brand.VivoBrand import com.telefonica.mistica.testutils.ScreenshotsTest +import com.telefonica.mistica.testutils.TestUtils import com.telefonica.mistica.testutils.TestUtils.getAllBrands import com.telefonica.mistica.testutils.TestUtils.isInverse import com.telefonica.mistica.util.getThemeColor @@ -37,7 +38,7 @@ internal class ButtonTest( ) : ScreenshotsTest() { private val intent = Intent(ApplicationProvider.getApplicationContext(), DummyActivity::class.java).apply { - this.putExtra(EXTRA_THEME, brand.getBaseThemeForBrand()) + this.putExtra(EXTRA_THEME, TestUtils.getBaseThemeForBrand(brand)) } @get:Rule @@ -105,17 +106,6 @@ internal class ButtonTest( } } -@StyleRes -private fun Brand.getBaseThemeForBrand(): Int = when (this) { - MovistarBrand -> R.style.MisticaTheme_Movistar - VivoBrand -> R.style.MisticaTheme_Vivo - O2Brand -> R.style.MisticaTheme_O2 - BlauBrand -> R.style.MisticaTheme_Blau - TuBrand -> R.style.MisticaTheme_Tu - TelefonicaBrand -> R.style.MisticaTheme_Telefonica - else -> error("No tests defined for brand $this") -} - @LayoutRes private fun ButtonStyle.getButtonLayout(): Int = when (this) { ButtonStyle.PRIMARY -> R.layout.primary_button diff --git a/library/src/test/java/com/telefonica/mistica/compose/input/CheckBoxInputTest.kt b/library/src/test/java/com/telefonica/mistica/compose/input/CheckBoxInputTest.kt new file mode 100644 index 000000000..52d261458 --- /dev/null +++ b/library/src/test/java/com/telefonica/mistica/compose/input/CheckBoxInputTest.kt @@ -0,0 +1,100 @@ +package com.telefonica.mistica.compose.input + +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Surface +import androidx.compose.ui.Modifier +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.unit.dp +import com.telefonica.mistica.compose.theme.MisticaTheme +import com.telefonica.mistica.compose.theme.brand.Brand +import com.telefonica.mistica.testutils.ScreenshotsTest +import com.telefonica.mistica.testutils.TestUtils +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.ParameterizedRobolectricTestRunner +import org.robolectric.ParameterizedRobolectricTestRunner.Parameters + +@RunWith(ParameterizedRobolectricTestRunner::class) +class CheckBoxInputTest( + private val brand: Brand, + private val checked: Boolean, + private val isEnabled: Boolean, + private val errorText: String?, + private val darkTheme: Boolean, +) : ScreenshotsTest() { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun `CheckBoxInput compose screenshot`() { + composeTestRule.setContent { + MisticaTheme(brand = brand, darkTheme = darkTheme) { + Surface( + color = MisticaTheme.colors.background + ) { + CheckBoxInput( + modifier = Modifier.padding(16.dp), + text = "Some text with terms and conditions", + links = listOf( + TextLink(link = "terms and conditions", onLinkTapped = {}), + ), + checked = checked, + onCheckedChange = {}, + errorText = errorText, + enabled = isEnabled, + ) + } + } + } + + val screenshotSuffix = StringBuilder() + if (errorText != null) { + screenshotSuffix.append("_errorText") + } + if (!isEnabled) { + screenshotSuffix.append("_disabled") + } + + if (checked) { + screenshotSuffix.append("_checked") + } else { + screenshotSuffix.append("_unchecked") + } + + compareScreenshot( + node = composeTestRule.onRoot(), + component = "CheckBoxInput", + style = null, + brand = brand, + darkTheme = darkTheme, + extra = screenshotSuffix.toString() + ) + } + + companion object { + @Suppress("UNUSED") + @JvmStatic + @Parameters + fun brands(): List> { + val brands = TestUtils.getAllBrands() + val enabled = arrayOf(false, true) + val checked = arrayOf(false, true) + val errorTexts = arrayOf(null, "Some error text") + val darkTheme = arrayOf(false, true) + + return brands.flatMap { brand -> + enabled.flatMap { isEnabled -> + checked.flatMap { isChecked -> + errorTexts.flatMap { errorText -> + darkTheme.map { darkTheme -> + arrayOf(brand, isEnabled, isChecked, errorText, darkTheme) + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/library/src/test/java/com/telefonica/mistica/input/CheckBoxInputTest.kt b/library/src/test/java/com/telefonica/mistica/input/CheckBoxInputTest.kt new file mode 100644 index 000000000..783780b0b --- /dev/null +++ b/library/src/test/java/com/telefonica/mistica/input/CheckBoxInputTest.kt @@ -0,0 +1,121 @@ +package com.telefonica.mistica.input + +import android.content.Intent +import android.text.Spannable +import android.text.SpannableString +import android.text.Spanned +import android.text.method.LinkMovementMethod +import android.text.style.ClickableSpan +import android.view.View +import android.widget.FrameLayout +import androidx.test.core.app.ApplicationProvider +import androidx.test.espresso.Espresso +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.ext.junit.rules.activityScenarioRule +import com.telefonica.mistica.DummyActivity +import com.telefonica.mistica.R +import com.telefonica.mistica.compose.theme.brand.Brand +import com.telefonica.mistica.testutils.ScreenshotsTest +import com.telefonica.mistica.testutils.TestUtils +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.ParameterizedRobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(ParameterizedRobolectricTestRunner::class) +class CheckBoxInputTest( + private val brand: Brand, + private val checked: Boolean, + private val isEnabled: Boolean, + private val errorText: String?, +) : ScreenshotsTest() { + private val intent = Intent(ApplicationProvider.getApplicationContext(), DummyActivity::class.java).apply { + this.putExtra(DummyActivity.EXTRA_THEME, TestUtils.getBaseThemeForBrand(brand)) + } + + @get:Rule + val rule = activityScenarioRule(intent) + + @Test + fun `CheckBoxInput XML screenshot light`() { + `compare CheckBoxInput XML screenshot`(darkTheme = false) + } + + @Config(qualifiers = "+night") + @Test + fun `CheckBoxInput XML screenshot dark`() { + `compare CheckBoxInput XML screenshot`(darkTheme = true) + } + + private fun `compare CheckBoxInput XML screenshot`(darkTheme: Boolean) { + rule.scenario.onActivity { activity -> + val wrapper: FrameLayout = activity.findViewById(R.id.dummy_activity_wrapper) + val checkbox = CheckBoxInput(activity).apply { + setChecked(checked) + error = errorText + setText(createSpannableText()) + setMovementMethod(LinkMovementMethod.getInstance()) + isEnabled = this@CheckBoxInputTest.isEnabled + } + + wrapper.removeAllViews() + wrapper.addView(checkbox) + + val screenshotSuffix = StringBuilder() + if (errorText != null) { + screenshotSuffix.append("_errorText") + } + if (!isEnabled) { + screenshotSuffix.append("_disabled") + } + + if (checked) { + screenshotSuffix.append("_checked") + } else { + screenshotSuffix.append("_unchecked") + } + + compareScreenshot( + node = Espresso.onView(ViewMatchers.withId(R.id.dummy_activity_wrapper)), + component = "CheckBoxInputXML", + style = null, + brand = brand, + darkTheme = darkTheme, + extra = screenshotSuffix.toString() + ) + } + } + + private fun createSpannableText(): Spannable { + val termsAndConditions = "terms and conditions" + val message = "Some text with $termsAndConditions" + return SpannableString(message).apply { + setSpan(dummyClickableSpan, message.indexOf(termsAndConditions), message.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + } + } + + companion object { + @Suppress("UNUSED") + @JvmStatic + @ParameterizedRobolectricTestRunner.Parameters + fun brands(): List> { + val brands = TestUtils.getAllBrands() + val enabled = arrayOf(false, true) + val checked = arrayOf(false, true) + val errorTexts = arrayOf(null, "Some error text") + + return brands.flatMap { brand -> + enabled.flatMap { isEnabled -> + checked.flatMap { isChecked -> + errorTexts.map { errorText -> + arrayOf(brand, isEnabled, isChecked, errorText) + } + } + } + } + } + + private val dummyClickableSpan = object : ClickableSpan() { override fun onClick(widget: View) {} } + } +} \ No newline at end of file diff --git a/library/src/test/java/com/telefonica/mistica/testutils/ScreenshotUtils.kt b/library/src/test/java/com/telefonica/mistica/testutils/ScreenshotUtils.kt index 194906093..97024a7ea 100644 --- a/library/src/test/java/com/telefonica/mistica/testutils/ScreenshotUtils.kt +++ b/library/src/test/java/com/telefonica/mistica/testutils/ScreenshotUtils.kt @@ -8,7 +8,11 @@ object ScreenshotUtils { val componentOrTestName = if(component == null) { TestUtils.findRunningTestMethodName() } else { - "${component}_${style}" + var componentName = component + if (style != null) { + componentName += "_$style" + } + componentName } val brandValue = if (brand != null) { brand::class.java.simpleName diff --git a/library/src/test/java/com/telefonica/mistica/testutils/TestUtils.kt b/library/src/test/java/com/telefonica/mistica/testutils/TestUtils.kt index 2b6617432..08ebf66ea 100644 --- a/library/src/test/java/com/telefonica/mistica/testutils/TestUtils.kt +++ b/library/src/test/java/com/telefonica/mistica/testutils/TestUtils.kt @@ -1,7 +1,10 @@ package com.telefonica.mistica.testutils +import androidx.annotation.StyleRes +import com.telefonica.mistica.R import com.telefonica.mistica.compose.button.ButtonStyle import com.telefonica.mistica.compose.theme.brand.BlauBrand +import com.telefonica.mistica.compose.theme.brand.Brand import com.telefonica.mistica.compose.theme.brand.MovistarBrand import com.telefonica.mistica.compose.theme.brand.O2Brand import com.telefonica.mistica.compose.theme.brand.TelefonicaBrand @@ -45,4 +48,15 @@ object TestUtils { ButtonStyle.LINK_INVERSE -> true else -> false } + + @StyleRes + fun getBaseThemeForBrand(brand: Brand): Int = when (brand) { + MovistarBrand -> R.style.MisticaTheme_Movistar + VivoBrand -> R.style.MisticaTheme_Vivo + O2Brand -> R.style.MisticaTheme_O2 + BlauBrand -> R.style.MisticaTheme_Blau + TuBrand -> R.style.MisticaTheme_Tu + TelefonicaBrand -> R.style.MisticaTheme_Telefonica + else -> error("No tests defined for brand $this") + } } \ No newline at end of file