Repo created
This commit is contained in:
parent
a629de6271
commit
3cef7c5092
2161 changed files with 246605 additions and 2 deletions
38
core/ui/compose/designsystem/README.md
Normal file
38
core/ui/compose/designsystem/README.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
## Core - UI - Compose - Design system
|
||||
|
||||
Uses [`:core:ui:compose:theme`](../theme/README.md)
|
||||
|
||||
## Background
|
||||
|
||||
[Jetpack Compose](https://developer.android.com/jetpack/compose) is a declarative UI toolkit for Android that provides a modern and efficient way to build UIs for Android apps. In this context, design systems and atomic design can help designers and developers create more scalable, maintainable, and reusable UIs.
|
||||
|
||||
### Design system
|
||||
|
||||
A design system is a collection of guidelines, principles, and tools that help teams create consistent and cohesive visual designs and user experiences.
|
||||
It typically includes a set of reusable components, such as icons, typography, color palettes, and layouts, that can be combined and customized to create new designs.
|
||||
|
||||
The design system also provides documentation and resources for designers and developers to ensure that the designs are implemented consistently and efficiently across all platforms and devices.
|
||||
The goal of a design system is to streamline the design process, improve design quality, and maintain brand consistency.
|
||||
|
||||
An example is Google's [Material Design](https://m3.material.io/) that is used to develop cohesive apps.
|
||||
|
||||
### Atomic Design
|
||||
|
||||

|
||||
|
||||
Atomic design is a methodology for creating user interfaces (UI) in a design system by breaking them down into smaller, reusable components.
|
||||
These components are classified into five categories based on their level of abstraction: **atoms**, **molecules**, **organisms**, **templates**, and **pages**.
|
||||
|
||||
- **Atoms** are the smallest building blocks, such as buttons, labels, and input fields and could be combined to create more complex components.
|
||||
- **Molecules** are groups of atoms that work together, like search bars, forms or menus
|
||||
- **Organisms** are more complex components that combine molecules and atoms, such as headers or cards.
|
||||
- **Templates** are pages with placeholders for components
|
||||
- **Pages** are the final UI
|
||||
|
||||
By using atomic design, designers and developers can create more consistent and reusable UIs.
|
||||
This can save time and improve the overall quality, as well as facilitate collaboration between team members.
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
- [Atomic Design Methodology | Atomic Design by Brad Frost](https://atomicdesign.bradfrost.com/chapter-2/)
|
||||
- [Atomic Design: Getting Started | Blog | We Are Mobile First](https://www.wearemobilefirst.com/blog/atomic-design)
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 17 KiB |
16
core/ui/compose/designsystem/build.gradle.kts
Normal file
16
core/ui/compose/designsystem/build.gradle.kts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
plugins {
|
||||
id(ThunderbirdPlugins.Library.androidCompose)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "app.k9mail.core.ui.compose.designsystem"
|
||||
resourcePrefix = "designsystem_"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.core.ui.compose.theme)
|
||||
implementation(libs.androidx.compose.material)
|
||||
implementation(libs.androidx.compose.material.icons.extended)
|
||||
|
||||
testImplementation(projects.core.ui.compose.testing)
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Surface as MaterialSurface
|
||||
|
||||
@Composable
|
||||
fun Background(
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
MaterialSurface(
|
||||
modifier = modifier,
|
||||
content = content,
|
||||
color = MainTheme.colors.background,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun BackgroundPreview() {
|
||||
PreviewWithThemes {
|
||||
Background(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
content = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Checkbox as MaterialCheckbox
|
||||
|
||||
@Composable
|
||||
fun Checkbox(
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
MaterialCheckbox(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun CheckboxPreview() {
|
||||
PreviewWithThemes {
|
||||
Checkbox(
|
||||
checked = true,
|
||||
onCheckedChange = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun CheckboxDisabledPreview() {
|
||||
PreviewWithThemes {
|
||||
Checkbox(
|
||||
checked = true,
|
||||
onCheckedChange = {},
|
||||
enabled = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Surface as MaterialSurface
|
||||
|
||||
@Composable
|
||||
fun Surface(
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = MainTheme.colors.surface,
|
||||
elevation: Dp = MainTheme.elevations.default,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
MaterialSurface(
|
||||
modifier = modifier,
|
||||
content = content,
|
||||
elevation = elevation,
|
||||
color = color,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun SurfacePreview() {
|
||||
PreviewWithThemes {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
content = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.button
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextButton
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Button as MaterialButton
|
||||
|
||||
@Composable
|
||||
fun Button(
|
||||
text: String,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
|
||||
) {
|
||||
MaterialButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
colors = ButtonDefaults.buttonColors(),
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
TextButton(text = text)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun ButtonPreview() {
|
||||
PreviewWithThemes {
|
||||
Button(
|
||||
text = "Button",
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun ButtonDisabledPreview() {
|
||||
PreviewWithThemes {
|
||||
Button(
|
||||
text = "ButtonDisabled",
|
||||
onClick = {},
|
||||
enabled = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.button
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextButton
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.OutlinedButton as MaterialOutlinedButton
|
||||
|
||||
@Composable
|
||||
fun ButtonOutlined(
|
||||
text: String,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
|
||||
) {
|
||||
MaterialOutlinedButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
colors = ButtonDefaults.outlinedButtonColors(),
|
||||
border = BorderStroke(
|
||||
width = 1.dp,
|
||||
color = if (enabled) {
|
||||
MainTheme.colors.primary
|
||||
} else {
|
||||
MainTheme.colors.onSurface.copy(
|
||||
alpha = 0.12f,
|
||||
)
|
||||
},
|
||||
),
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
TextButton(text = text)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun ButtonOutlinedPreview() {
|
||||
PreviewWithThemes {
|
||||
ButtonOutlined(
|
||||
text = "ButtonOutlined",
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun ButtonOutlinedDisabledPreview() {
|
||||
PreviewWithThemes {
|
||||
ButtonOutlined(
|
||||
text = "ButtonOutlinedDisabled",
|
||||
onClick = {},
|
||||
enabled = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.button
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextButton
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.TextButton as MaterialTextButton
|
||||
|
||||
@Composable
|
||||
fun ButtonText(
|
||||
text: String,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
|
||||
) {
|
||||
MaterialTextButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
colors = ButtonDefaults.textButtonColors(),
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
TextButton(text = text)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun ButtonTextPreview() {
|
||||
PreviewWithThemes {
|
||||
ButtonText(
|
||||
text = "ButtonText",
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun ButtonTextDisabledPreview() {
|
||||
PreviewWithThemes {
|
||||
ButtonText(
|
||||
text = "ButtonTextDisabled",
|
||||
onClick = {},
|
||||
enabled = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.text
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Text as MaterialText
|
||||
|
||||
@Composable
|
||||
fun TextBody1(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MaterialText(
|
||||
text = text,
|
||||
style = MainTheme.typography.body1,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextBody1Preview() {
|
||||
PreviewWithThemes {
|
||||
TextBody1(text = "TextBody1")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.text
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Text as MaterialText
|
||||
|
||||
@Composable
|
||||
fun TextBody2(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MaterialText(
|
||||
text = text,
|
||||
style = MainTheme.typography.body2,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextBody2Preview() {
|
||||
PreviewWithThemes {
|
||||
TextBody2(text = "TextBody2")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.text
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Text as MaterialText
|
||||
|
||||
@Composable
|
||||
fun TextButton(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MaterialText(
|
||||
text = text.uppercase(),
|
||||
style = MainTheme.typography.button,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextButtonPreview() {
|
||||
PreviewWithThemes {
|
||||
TextButton(text = "TextButton")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.text
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Text as MaterialText
|
||||
|
||||
@Composable
|
||||
fun TextCaption(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MaterialText(
|
||||
text = text,
|
||||
style = MainTheme.typography.caption,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextCaptionPreview() {
|
||||
PreviewWithThemes {
|
||||
TextCaption(text = "TextCaption")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.text
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Text as MaterialText
|
||||
|
||||
@Composable
|
||||
fun TextHeadline1(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MaterialText(
|
||||
text = text,
|
||||
style = MainTheme.typography.h1,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextHeadline1Preview() {
|
||||
PreviewWithThemes {
|
||||
TextHeadline1(text = "TextHeadline1")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.text
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Text as MaterialText
|
||||
|
||||
@Composable
|
||||
fun TextHeadline2(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MaterialText(
|
||||
text = text,
|
||||
style = MainTheme.typography.h2,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextHeadline2Preview() {
|
||||
PreviewWithThemes {
|
||||
TextHeadline2(text = "TextHeadline2")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.text
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Text as MaterialText
|
||||
|
||||
@Composable
|
||||
fun TextHeadline3(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MaterialText(
|
||||
text = text,
|
||||
style = MainTheme.typography.h3,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextHeadline3Preview() {
|
||||
PreviewWithThemes {
|
||||
TextHeadline3(text = "TextHeadline3")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.text
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Text as MaterialText
|
||||
|
||||
@Composable
|
||||
fun TextHeadline4(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MaterialText(
|
||||
text = text,
|
||||
style = MainTheme.typography.h4,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextHeadline4Preview() {
|
||||
PreviewWithThemes {
|
||||
TextHeadline4(text = "TextHeadline4")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.text
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Text as MaterialText
|
||||
|
||||
@Composable
|
||||
fun TextHeadline5(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MaterialText(
|
||||
text = text,
|
||||
style = MainTheme.typography.h5,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextHeadline5Preview() {
|
||||
PreviewWithThemes {
|
||||
TextHeadline5(text = "TextHeadline5")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.text
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Text as MaterialText
|
||||
|
||||
@Composable
|
||||
fun TextHeadline6(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MaterialText(
|
||||
text = text,
|
||||
style = MainTheme.typography.h6,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextHeadline6Preview() {
|
||||
PreviewWithThemes {
|
||||
TextHeadline6(text = "TextHeadline6")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.text
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Text as MaterialText
|
||||
|
||||
@Composable
|
||||
fun TextOverline(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MaterialText(
|
||||
text = text.uppercase(),
|
||||
style = MainTheme.typography.overline,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextOverlinePreview() {
|
||||
PreviewWithThemes {
|
||||
TextOverline(text = "TextOverline")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.text
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Text as MaterialText
|
||||
|
||||
@Composable
|
||||
fun TextSubtitle1(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MaterialText(
|
||||
text = text,
|
||||
style = MainTheme.typography.subtitle1,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextSubtitle1Preview() {
|
||||
PreviewWithThemes {
|
||||
TextSubtitle1(text = "TextSubtitle1")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.text
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Text as MaterialText
|
||||
|
||||
@Composable
|
||||
fun TextSubtitle2(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MaterialText(
|
||||
text = text,
|
||||
style = MainTheme.typography.subtitle2,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextSubtitle2Preview() {
|
||||
PreviewWithThemes {
|
||||
TextSubtitle2(text = "TextSubtitle2")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.textfield
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Visibility
|
||||
import androidx.compose.material.icons.filled.VisibilityOff
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.R
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.OutlinedTextField as MaterialOutlinedTextField
|
||||
|
||||
@Composable
|
||||
fun PasswordTextFieldOutlined(
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
label: String? = null,
|
||||
isError: Boolean = false,
|
||||
) {
|
||||
var passwordVisibilityState by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
MaterialOutlinedTextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
label = selectLabel(label),
|
||||
trailingIcon = selectTrailingIcon(
|
||||
isEnabled = enabled,
|
||||
isPasswordVisible = passwordVisibilityState,
|
||||
onClick = { passwordVisibilityState = !passwordVisibilityState },
|
||||
),
|
||||
isError = isError,
|
||||
visualTransformation = selectVisualTransformation(
|
||||
isEnabled = enabled,
|
||||
isPasswordVisible = passwordVisibilityState,
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
|
||||
singleLine = true,
|
||||
)
|
||||
}
|
||||
|
||||
private fun selectLabel(label: String?): @Composable (() -> Unit)? {
|
||||
return if (label != null) {
|
||||
{
|
||||
Text(text = label)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectTrailingIcon(
|
||||
isEnabled: Boolean,
|
||||
isPasswordVisible: Boolean,
|
||||
onClick: () -> Unit,
|
||||
hasTrailingIcon: Boolean = true,
|
||||
): @Composable (() -> Unit)? {
|
||||
return if (hasTrailingIcon) {
|
||||
{
|
||||
val image = if (isShowPasswordAllowed(isEnabled, isPasswordVisible)) {
|
||||
Icons.Filled.Visibility
|
||||
} else {
|
||||
Icons.Filled.VisibilityOff
|
||||
}
|
||||
|
||||
val description = if (isShowPasswordAllowed(isEnabled, isPasswordVisible)) {
|
||||
stringResource(id = R.string.designsystem_atom_password_textfield_hide_password)
|
||||
} else {
|
||||
stringResource(id = R.string.designsystem_atom_password_textfield_show_password)
|
||||
}
|
||||
|
||||
IconButton(onClick = onClick) {
|
||||
Icon(imageVector = image, contentDescription = description)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectVisualTransformation(
|
||||
isEnabled: Boolean,
|
||||
isPasswordVisible: Boolean,
|
||||
): VisualTransformation {
|
||||
return if (isShowPasswordAllowed(isEnabled, isPasswordVisible)) {
|
||||
VisualTransformation.None
|
||||
} else {
|
||||
PasswordVisualTransformation()
|
||||
}
|
||||
}
|
||||
|
||||
private fun isShowPasswordAllowed(isEnabled: Boolean, isPasswordVisible: Boolean) = isEnabled && isPasswordVisible
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun PasswordTextFieldOutlinedPreview() {
|
||||
PreviewWithThemes {
|
||||
PasswordTextFieldOutlined(
|
||||
value = "Input text",
|
||||
onValueChange = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun PasswordTextFieldOutlinedWithLabelPreview() {
|
||||
PreviewWithThemes {
|
||||
PasswordTextFieldOutlined(
|
||||
value = "Input text",
|
||||
label = "Label",
|
||||
onValueChange = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun PasswordTextFieldOutlinedDisabledPreview() {
|
||||
PreviewWithThemes {
|
||||
PasswordTextFieldOutlined(
|
||||
value = "Input text",
|
||||
onValueChange = {},
|
||||
enabled = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun PasswordTextFieldOutlinedErrorPreview() {
|
||||
PreviewWithThemes {
|
||||
PasswordTextFieldOutlined(
|
||||
value = "Input text",
|
||||
onValueChange = {},
|
||||
isError = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.textfield
|
||||
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.OutlinedTextField as MaterialOutlinedTextField
|
||||
|
||||
@Composable
|
||||
fun TextFieldOutlined(
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
label: String? = null,
|
||||
isError: Boolean = false,
|
||||
) {
|
||||
MaterialOutlinedTextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
label = selectLabel(label),
|
||||
isError = isError,
|
||||
)
|
||||
}
|
||||
|
||||
private fun selectLabel(label: String?): @Composable (() -> Unit)? {
|
||||
return if (label != null) {
|
||||
{
|
||||
Text(text = label)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextFieldOutlinedPreview() {
|
||||
PreviewWithThemes {
|
||||
TextFieldOutlined(
|
||||
value = "Input text",
|
||||
onValueChange = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextFieldOutlinedWithLabelPreview() {
|
||||
PreviewWithThemes {
|
||||
TextFieldOutlined(
|
||||
value = "Input text",
|
||||
label = "Label",
|
||||
onValueChange = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextFieldOutlinedDisabledPreview() {
|
||||
PreviewWithThemes {
|
||||
TextFieldOutlined(
|
||||
value = "Input text",
|
||||
onValueChange = {},
|
||||
enabled = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
internal fun TextFieldOutlinedErrorPreview() {
|
||||
PreviewWithThemes {
|
||||
TextFieldOutlined(
|
||||
value = "Input text",
|
||||
onValueChange = {},
|
||||
isError = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.template
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import app.k9mail.core.ui.compose.theme.K9Theme
|
||||
|
||||
/**
|
||||
* The [LazyColumnWithFooter] composable creates a [LazyColumn] with a footer.
|
||||
*
|
||||
* @param modifier The modifier to be applied to the layout.
|
||||
* @param verticalArrangement The vertical arrangement of the children.
|
||||
* @param horizontalAlignment The horizontal alignment of the children.
|
||||
* @param footer The footer to be displayed at the bottom of the [LazyColumn].
|
||||
* @param content The content of the [LazyColumn].
|
||||
*/
|
||||
@Composable
|
||||
fun LazyColumnWithFooter(
|
||||
modifier: Modifier = Modifier,
|
||||
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
|
||||
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
||||
footer: @Composable () -> Unit = {},
|
||||
content: LazyListScope.() -> Unit,
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
verticalArrangement = verticalArrangementWithFooter(verticalArrangement),
|
||||
horizontalAlignment = horizontalAlignment,
|
||||
) {
|
||||
content()
|
||||
item { footer() }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun verticalArrangementWithFooter(verticalArrangement: Arrangement.Vertical) = remember {
|
||||
object : Arrangement.Vertical {
|
||||
override fun Density.arrange(
|
||||
totalSize: Int,
|
||||
sizes: IntArray,
|
||||
outPositions: IntArray,
|
||||
) {
|
||||
val innerSizes = sizes.dropLast(1).toIntArray()
|
||||
val footerSize = sizes.last()
|
||||
val innerTotalSize = totalSize - footerSize
|
||||
|
||||
with(verticalArrangement) {
|
||||
arrange(
|
||||
totalSize = innerTotalSize,
|
||||
sizes = innerSizes,
|
||||
outPositions = outPositions,
|
||||
)
|
||||
}
|
||||
|
||||
outPositions[outPositions.lastIndex] = totalSize - footerSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
internal fun LazyColumnWithFooterPreview() {
|
||||
K9Theme {
|
||||
Surface {
|
||||
LazyColumnWithFooter(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(32.dp, Alignment.CenterVertically),
|
||||
footer = { Text(text = "Footer") },
|
||||
) {
|
||||
items(10) {
|
||||
Text(text = "Item $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.template
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.requiredHeight
|
||||
import androidx.compose.foundation.layout.requiredWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.k9mail.core.ui.compose.common.DevicePreviews
|
||||
import app.k9mail.core.ui.compose.common.window.WindowSizeClass
|
||||
import app.k9mail.core.ui.compose.common.window.getWindowSizeInfo
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import app.k9mail.core.ui.compose.theme.K9Theme
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
|
||||
/**
|
||||
* The [ResponsiveContent] composable automatically adapts its child content to different screen sizes and resolutions,
|
||||
* providing a responsive layout for a better user experience.
|
||||
*
|
||||
* It uses the [WindowSizeClass] (Compact, Medium, or Expanded) to make appropriate layout adjustments.
|
||||
*
|
||||
* @param modifier The modifier to be applied to the layout.
|
||||
* @param content The content to be displayed.
|
||||
*/
|
||||
@Composable
|
||||
fun ResponsiveContent(
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val windowSizeClass = getWindowSizeInfo()
|
||||
|
||||
when (windowSizeClass.screenWidthSizeClass) {
|
||||
WindowSizeClass.Compact -> CompactContent(modifier = modifier, content = content)
|
||||
WindowSizeClass.Medium -> MediumContent(modifier = modifier, content = content)
|
||||
WindowSizeClass.Expanded -> ExpandedContent(modifier = modifier, content = content)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CompactContent(
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.then(modifier),
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MediumContent(
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.then(modifier),
|
||||
contentAlignment = Alignment.TopCenter,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.requiredWidth(WindowSizeClass.COMPACT_MAX_WIDTH.dp),
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExpandedContent(
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
when (getWindowSizeInfo().screenHeightSizeClass) {
|
||||
WindowSizeClass.Compact -> MediumContent(modifier, content)
|
||||
WindowSizeClass.Medium -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.then(modifier),
|
||||
contentAlignment = Alignment.TopCenter,
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.requiredWidth(WindowSizeClass.MEDIUM_MAX_WIDTH.dp),
|
||||
elevation = MainTheme.elevations.raised,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowSizeClass.Expanded -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.then(modifier),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.requiredWidth(WindowSizeClass.MEDIUM_MAX_WIDTH.dp)
|
||||
.requiredHeight(WindowSizeClass.MEDIUM_MAX_HEIGHT.dp),
|
||||
elevation = MainTheme.elevations.raised,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@DevicePreviews
|
||||
internal fun ResponsiveContentPreview() {
|
||||
K9Theme {
|
||||
Surface {
|
||||
ResponsiveContent {
|
||||
Surface(
|
||||
color = MainTheme.colors.info,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="designsystem_atom_password_textfield_hide_password">Hide password</string>
|
||||
<string name="designsystem_atom_password_textfield_show_password">Show password</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.textfield
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteraction
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import app.k9mail.core.ui.compose.designsystem.R
|
||||
import app.k9mail.core.ui.compose.testing.ComposeTest
|
||||
import org.junit.Test
|
||||
|
||||
private const val PASSWORD = "Password input"
|
||||
|
||||
class PasswordTextFieldOutlinedKtTest : ComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `should not display password by default`() = runComposeTest {
|
||||
setContent {
|
||||
PasswordTextFieldOutlined(
|
||||
value = PASSWORD,
|
||||
onValueChange = {},
|
||||
)
|
||||
}
|
||||
|
||||
onNodeWithText(PASSWORD).assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should display password when show password is clicked`() = runComposeTest {
|
||||
setContent {
|
||||
PasswordTextFieldOutlined(
|
||||
value = PASSWORD,
|
||||
onValueChange = {},
|
||||
)
|
||||
}
|
||||
|
||||
onShowPasswordNode().performClick()
|
||||
|
||||
onNodeWithText(PASSWORD).assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not display password when hide password is clicked`() = runComposeTest {
|
||||
setContent {
|
||||
PasswordTextFieldOutlined(
|
||||
value = PASSWORD,
|
||||
onValueChange = {},
|
||||
)
|
||||
}
|
||||
onShowPasswordNode().performClick()
|
||||
|
||||
onHidePasswordNode().performClick()
|
||||
|
||||
onNodeWithText(PASSWORD).assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should display hide password icon when show password is clicked`() = runComposeTest {
|
||||
setContent {
|
||||
PasswordTextFieldOutlined(
|
||||
value = PASSWORD,
|
||||
onValueChange = {},
|
||||
)
|
||||
}
|
||||
|
||||
onShowPasswordNode().performClick()
|
||||
|
||||
onHidePasswordNode().assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should display show password icon when hide password icon is clicked`() = runComposeTest {
|
||||
setContent {
|
||||
PasswordTextFieldOutlined(
|
||||
value = PASSWORD,
|
||||
onValueChange = {},
|
||||
)
|
||||
}
|
||||
onShowPasswordNode().performClick()
|
||||
|
||||
onHidePasswordNode().performClick()
|
||||
|
||||
onShowPasswordNode().assertIsDisplayed()
|
||||
}
|
||||
|
||||
private fun SemanticsNodeInteractionsProvider.onShowPasswordNode(): SemanticsNodeInteraction {
|
||||
return onNodeWithContentDescription(
|
||||
getString(R.string.designsystem_atom_password_textfield_show_password),
|
||||
)
|
||||
}
|
||||
|
||||
private fun SemanticsNodeInteractionsProvider.onHidePasswordNode(): SemanticsNodeInteraction {
|
||||
return onNodeWithContentDescription(
|
||||
getString(R.string.designsystem_atom_password_textfield_hide_password),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.atom.textfield
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.assertIsEnabled
|
||||
import androidx.compose.ui.test.assertIsNotEnabled
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import app.k9mail.core.ui.compose.testing.ComposeTest
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.ParameterizedRobolectricTestRunner
|
||||
|
||||
private const val VALUE = "Input text"
|
||||
private const val LABEL = "Label"
|
||||
|
||||
data class TextFieldTestData(
|
||||
val name: String,
|
||||
val content: @Composable (
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier,
|
||||
enabled: Boolean?,
|
||||
label: String?,
|
||||
) -> Unit,
|
||||
)
|
||||
|
||||
@RunWith(ParameterizedRobolectricTestRunner::class)
|
||||
class TextFieldKtTest(
|
||||
data: TextFieldTestData,
|
||||
) : ComposeTest() {
|
||||
|
||||
private val testSubjectName = data.name
|
||||
private val testSubject = data.content
|
||||
|
||||
@Test
|
||||
fun `should call onValueChange when value changes`() = runComposeTest {
|
||||
var value = VALUE
|
||||
setContent {
|
||||
testSubject(
|
||||
value = value,
|
||||
onValueChange = { value = it },
|
||||
modifier = Modifier.testTag(testSubjectName),
|
||||
enabled = null,
|
||||
label = null,
|
||||
)
|
||||
}
|
||||
|
||||
onNodeWithTag(testSubjectName).performClick()
|
||||
onNodeWithTag(testSubjectName).performTextInput(" + added text")
|
||||
|
||||
assertThat(value).isEqualTo("$VALUE + added text")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should be enabled by default`() = runComposeTest {
|
||||
setContent {
|
||||
testSubject(
|
||||
value = VALUE,
|
||||
onValueChange = {},
|
||||
modifier = Modifier.testTag(testSubjectName),
|
||||
enabled = null,
|
||||
label = null,
|
||||
)
|
||||
}
|
||||
|
||||
onNodeWithTag(testSubjectName).assertIsEnabled()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should be disabled when enabled is false`() = runComposeTest {
|
||||
setContent {
|
||||
testSubject(
|
||||
value = VALUE,
|
||||
onValueChange = {},
|
||||
modifier = Modifier.testTag(testSubjectName),
|
||||
enabled = false,
|
||||
label = null,
|
||||
)
|
||||
}
|
||||
|
||||
onNodeWithTag(testSubjectName).assertIsNotEnabled()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should show label when label is not null`() = runComposeTest {
|
||||
setContent {
|
||||
testSubject(
|
||||
value = VALUE,
|
||||
onValueChange = {},
|
||||
modifier = Modifier.testTag(testSubjectName),
|
||||
enabled = null,
|
||||
label = LABEL,
|
||||
)
|
||||
}
|
||||
|
||||
onNodeWithText(LABEL).assertIsDisplayed()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@ParameterizedRobolectricTestRunner.Parameters(name = "{0}")
|
||||
fun data(): List<TextFieldTestData> = listOf(
|
||||
TextFieldTestData(
|
||||
name = "TextFieldOutlined",
|
||||
content = { value, onValueChange, modifier, enabled, label ->
|
||||
if (enabled != null) {
|
||||
TextFieldOutlined(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
label = label,
|
||||
)
|
||||
} else {
|
||||
TextFieldOutlined(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier,
|
||||
label = label,
|
||||
)
|
||||
}
|
||||
},
|
||||
),
|
||||
TextFieldTestData(
|
||||
name = "PasswordTextFieldOutlined",
|
||||
content = { value, onValueChange, modifier, enabled, label ->
|
||||
if (enabled != null) {
|
||||
PasswordTextFieldOutlined(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
label = label,
|
||||
)
|
||||
} else {
|
||||
PasswordTextFieldOutlined(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier,
|
||||
label = label,
|
||||
)
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue