Merge pull request #7517 from thunderbird/compose_and_koin

Add helper for composable previews whose children are using `koinInject()`
This commit is contained in:
cketti 2024-01-16 13:19:50 +01:00 committed by GitHub
commit 5ebf7e50af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 102 additions and 30 deletions

View file

@ -11,4 +11,5 @@ dependencies {
implementation(libs.androidx.compose.activity) implementation(libs.androidx.compose.activity)
testImplementation(projects.core.ui.compose.testing) testImplementation(projects.core.ui.compose.testing)
testImplementation(projects.core.ui.compose.designsystem)
} }

View file

@ -0,0 +1,27 @@
package app.k9mail.core.ui.compose.common.koin
import androidx.compose.runtime.Composable
import org.koin.compose.KoinContext
import org.koin.core.Koin
import org.koin.dsl.ModuleDeclaration
import org.koin.dsl.module
/**
* Helper to make Compose previews work when some dependencies are injected via Koin.
*/
fun koinPreview(moduleDeclaration: ModuleDeclaration): KoinPreview {
val koin = Koin().apply {
loadModules(listOf(module(moduleDeclaration = moduleDeclaration)))
}
return KoinPreview(koin)
}
class KoinPreview internal constructor(private val koin: Koin) {
@Composable
infix fun WithContent(content: @Composable () -> Unit) {
KoinContext(context = koin) {
content()
}
}
}

View file

@ -0,0 +1,32 @@
package app.k9mail.core.ui.compose.common.koin
import androidx.compose.runtime.Composable
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBody1
import app.k9mail.core.ui.compose.testing.ComposeTest
import app.k9mail.core.ui.compose.testing.onNodeWithText
import kotlin.test.Test
import org.koin.compose.koinInject
class KoinPreviewTest : ComposeTest() {
@Test
fun `koinPreview should make dependencies available in WithContent block`() = runComposeTest {
val injectString = "Test"
setContent {
koinPreview {
factory { injectString }
} WithContent {
TestComposable()
}
}
onNodeWithText(injectString).assertExists()
}
}
@Composable
private fun TestComposable(
injected: String = koinInject(),
) {
TextBody1(text = injected)
}

View file

@ -17,6 +17,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.k9mail.core.ui.compose.common.baseline.withBaseline import app.k9mail.core.ui.compose.common.baseline.withBaseline
import app.k9mail.core.ui.compose.common.koin.koinPreview
import app.k9mail.core.ui.compose.common.resources.annotatedStringResource import app.k9mail.core.ui.compose.common.resources.annotatedStringResource
import app.k9mail.core.ui.compose.common.text.bold import app.k9mail.core.ui.compose.common.text.bold
import app.k9mail.core.ui.compose.designsystem.atom.Icon import app.k9mail.core.ui.compose.designsystem.atom.Icon
@ -38,13 +39,12 @@ internal fun ServerCertificateErrorContent(
innerPadding: PaddingValues, innerPadding: PaddingValues,
state: State, state: State,
scrollState: ScrollState, scrollState: ScrollState,
serverNameFormatter: ServerNameFormatter = koinInject(),
) { ) {
ResponsiveWidthContainer(modifier = Modifier.padding(innerPadding)) { ResponsiveWidthContainer(modifier = Modifier.padding(innerPadding)) {
Column( Column(
modifier = Modifier.verticalScroll(scrollState), modifier = Modifier.verticalScroll(scrollState),
) { ) {
CertificateErrorOverview(state, serverNameFormatter) CertificateErrorOverview(state)
AnimatedContent( AnimatedContent(
targetState = state.isShowServerCertificate, targetState = state.isShowServerCertificate,
@ -53,7 +53,6 @@ internal fun ServerCertificateErrorContent(
if (isShowServerCertificate) { if (isShowServerCertificate) {
ServerCertificateView( ServerCertificateView(
serverCertificateProperties = state.certificateError!!.serverCertificateProperties, serverCertificateProperties = state.certificateError!!.serverCertificateProperties,
serverNameFormatter = serverNameFormatter,
) )
} }
} }
@ -62,7 +61,7 @@ internal fun ServerCertificateErrorContent(
} }
@Composable @Composable
private fun CertificateErrorOverview(state: State, serverNameFormatter: ServerNameFormatter) { private fun CertificateErrorOverview(state: State) {
Column( Column(
modifier = Modifier.padding(all = MainTheme.spacings.double), modifier = Modifier.padding(all = MainTheme.spacings.double),
) { ) {
@ -72,7 +71,7 @@ private fun CertificateErrorOverview(state: State, serverNameFormatter: ServerNa
Spacer(modifier = Modifier.height(MainTheme.spacings.quadruple)) Spacer(modifier = Modifier.height(MainTheme.spacings.quadruple))
state.certificateError?.let { certificateError -> state.certificateError?.let { certificateError ->
CertificateErrorDescription(certificateError, serverNameFormatter) CertificateErrorDescription(certificateError)
} }
} }
} }
@ -104,7 +103,7 @@ private fun WarningTitle() {
@Composable @Composable
private fun CertificateErrorDescription( private fun CertificateErrorDescription(
certificateError: FormattedServerCertificateError, certificateError: FormattedServerCertificateError,
serverNameFormatter: ServerNameFormatter, serverNameFormatter: ServerNameFormatter = koinInject(),
) { ) {
TextBody1( TextBody1(
text = annotatedStringResource( text = annotatedStringResource(
@ -135,12 +134,15 @@ internal fun ServerCertificateErrorContentPreview() {
), ),
) )
K9Theme { koinPreview {
ServerCertificateErrorContent( factory<ServerNameFormatter> { DefaultServerNameFormatter() }
innerPadding = PaddingValues(all = 0.dp), } WithContent {
state = state, K9Theme {
scrollState = rememberScrollState(), ServerCertificateErrorContent(
serverNameFormatter = DefaultServerNameFormatter(), innerPadding = PaddingValues(all = 0.dp),
) state = state,
scrollState = rememberScrollState(),
)
}
} }
} }

View file

@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.k9mail.core.ui.compose.common.annotation.PreviewDevices import app.k9mail.core.ui.compose.common.annotation.PreviewDevices
import app.k9mail.core.ui.compose.common.koin.koinPreview
import app.k9mail.core.ui.compose.common.mvi.observe import app.k9mail.core.ui.compose.common.mvi.observe
import app.k9mail.core.ui.compose.designsystem.atom.Surface import app.k9mail.core.ui.compose.designsystem.atom.Surface
import app.k9mail.core.ui.compose.designsystem.atom.button.Button import app.k9mail.core.ui.compose.designsystem.atom.button.Button
@ -168,16 +169,20 @@ internal fun ServerCertificateErrorScreenK9Preview() {
certificateChain = listOf(certificate), certificateChain = listOf(certificate),
) )
K9Theme { koinPreview {
ServerCertificateErrorScreen( factory<ServerNameFormatter> { DefaultServerNameFormatter() }
onCertificateAccepted = {}, } WithContent {
onBack = {}, K9Theme {
viewModel = ServerCertificateErrorViewModel( ServerCertificateErrorScreen(
addServerCertificateException = { _, _, _ -> }, onCertificateAccepted = {},
certificateErrorRepository = InMemoryServerCertificateErrorRepository(serverCertificateError), onBack = {},
formatServerCertificateError = FormatServerCertificateError(), viewModel = ServerCertificateErrorViewModel(
initialState = State(isShowServerCertificate = false), addServerCertificateException = { _, _, _ -> },
), certificateErrorRepository = InMemoryServerCertificateErrorRepository(serverCertificateError),
) formatServerCertificateError = FormatServerCertificateError(),
initialState = State(isShowServerCertificate = false),
),
)
}
} }
} }

View file

@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import app.k9mail.core.ui.compose.common.koin.koinPreview
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBody1 import app.k9mail.core.ui.compose.designsystem.atom.text.TextBody1
import app.k9mail.core.ui.compose.designsystem.atom.text.TextHeadline6 import app.k9mail.core.ui.compose.designsystem.atom.text.TextHeadline6
import app.k9mail.core.ui.compose.designsystem.atom.text.TextOverline import app.k9mail.core.ui.compose.designsystem.atom.text.TextOverline
@ -17,12 +18,13 @@ import app.k9mail.core.ui.compose.theme.K9Theme
import app.k9mail.core.ui.compose.theme.MainTheme import app.k9mail.core.ui.compose.theme.MainTheme
import app.k9mail.feature.account.server.certificate.R import app.k9mail.feature.account.server.certificate.R
import app.k9mail.feature.account.server.certificate.domain.entity.ServerCertificateProperties import app.k9mail.feature.account.server.certificate.domain.entity.ServerCertificateProperties
import org.koin.compose.koinInject
@Composable @Composable
internal fun ServerCertificateView( internal fun ServerCertificateView(
serverCertificateProperties: ServerCertificateProperties, serverCertificateProperties: ServerCertificateProperties,
serverNameFormatter: ServerNameFormatter,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
serverNameFormatter: ServerNameFormatter = koinInject(),
) { ) {
Column( Column(
modifier = modifier.padding( modifier = modifier.padding(
@ -108,10 +110,13 @@ internal fun ServerCertificateViewPreview() {
"ea6e2b8db2b9da9197d5112fb369fd006da545de", "ea6e2b8db2b9da9197d5112fb369fd006da545de",
) )
K9Theme { koinPreview {
ServerCertificateView( factory<ServerNameFormatter> { DefaultServerNameFormatter() }
serverCertificateProperties = serverCertificateProperties, } WithContent {
serverNameFormatter = DefaultServerNameFormatter(), K9Theme {
) ServerCertificateView(
serverCertificateProperties = serverCertificateProperties,
)
}
} }
} }