Skip to content

GT-2594 Setup a new compose renderer module #795

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: feature/compose
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ ij_kotlin_allow_trailing_comma=true
ij_kotlin_allow_trailing_comma_on_call_site=true
indent_size=4
ktlint_code_style=android_studio
ktlint_function_naming_ignore_when_annotated_with = Composable
ktlint_standard_class-signature = disabled
ktlint_standard_spacing-between-declarations-with-annotations=disabled
ktlint_standard_trailing-comma-on-call-site=disabled
Expand Down
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@
*.properties text
*.xml text
*.yml text

**/snapshots/**/*.png filter=lfs diff=lfs merge=lfs -text
4 changes: 3 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
lfs: 'true'
- name: Setup Java JDK
uses: actions/setup-java@v4
with:
Expand All @@ -147,7 +149,7 @@ jobs:
key: ${{ runner.os }}-maven-${{ github.sha }}
restore-keys: ${{ runner.os }}-maven-
- name: Run Android Unit Tests
run: ./gradlew test koverXmlReportRelease --scan --no-build-cache
run: ./gradlew test verifyPaparazzi koverXmlReport --scan --no-build-cache
- name: Codecov
uses: codecov/codecov-action@v5
with:
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/git-lfs-validation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: "Validate Git LFS"

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
validate-lfs-pointers:
name: "Validate Git LFS pointers"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: git lfs fsck --pointers
48 changes: 48 additions & 0 deletions .github/workflows/record-snapshots.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Record Snapshots

on:
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
record_snapshots:
name: Record Snapshots
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
lfs: 'true'
- name: Setup Java JDK
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version-file: ".java-version"
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Cache Konan
uses: actions/cache@v4
with:
path: ~/.konan
key: ${{ runner.os }}-konan-${{ github.sha }}
restore-keys: ${{ runner.os }}-konan-
- name: Cache Maven
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ github.sha }}
restore-keys: |
${{ runner.os }}-maven-
- name: Record snapshots
run: ./gradlew cleanRecordPaparazzi --scan
- name: Commit snapshots
run: |
git config --global user.name '${{ github.actor }}'
git config --global user.email '${{ github.actor }}@users.noreply.github.com'
git add .
git commit -am "Record updated snapshots" || echo "No changed snapshots"
git push
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ kotlin {
api(project(":module:analytics"))
api(project(":module:interop"))
api(project(":module:parser"))
api(project(":module:renderer"))
api(project(":module:state"))
api(project(":module:user-activity"))
}
Expand Down Expand Up @@ -60,6 +61,7 @@ kotlin {
export(project(":module:analytics"))
export(project(":module:interop"))
export(project(":module:parser"))
export(project(":module:renderer"))
export(project(":module:state"))
export(project(":module:user-activity"))
}
Expand Down
6 changes: 5 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ kotlin-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-te
kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
kotlin-kover-gradle = "org.jetbrains.kotlinx:kover-gradle-plugin:0.9.1"
ktlint-gradle = { module = "org.jlleitschuh.gradle:ktlint-gradle", version.ref = "ktlintGradle" }
ktlint-rules-compose = "io.nlopez.compose.rules:ktlint:0.4.22"
kustomExport = { module = "deezer.kustomexport:lib", version.ref = "kustomExport" }
kustomExport-compiler = { module = "deezer.kustomexport:compiler", version.ref = "kustomExport" }
kustomExport-coroutines = { module = "deezer.kustomexport:lib-coroutines", version.ref = "kustomExport" }
Expand All @@ -50,11 +51,14 @@ ktlint = { module = "com.pinterest.ktlint:ktlint-cli", version.ref = "ktlint" }
[bundles]
android-test-framework = ["androidx-test-junit", "robolectric"]
common-test-framework = ["kotlin-coroutines-test"]
ktlint-rulesets = []
ktlint-rulesets = ["ktlint-rules-compose"]

[plugins]
compose = { id = "org.jetbrains.compose", version = "1.7.3" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
goncalossilvaResources = { id = "com.goncalossilva.resources", version.ref = "goncalossilvaResources" }
grgit = { id = "org.ajoberstar.grgit", version = "5.3.0" }
ksp = { id = "com.google.devtools.ksp", version = "2.1.21-2.0.1" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlintGradle" }
npm-publish = { id = "dev.petuska.npm.publish", version.ref = "npmPublish" }
paparazzi = { id = "app.cash.paparazzi", version = "2.0.0-alpha01" }
2 changes: 1 addition & 1 deletion module/parser/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ kotlin {
implementation(libs.kotlin.coroutines.core)

api(libs.colormath)
api(libs.gtoSupport.fluidsonic.locale)
implementation(libs.androidx.annotation)
implementation(libs.fluidLocale)
implementation(libs.gtoSupport.fluidsonic.locale)
implementation(libs.kermit)
implementation(libs.kustomExport)
implementation(libs.kustomExport.coroutines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ class Button : Content, HasAnalyticsEvents, Clickable {
}

@RestrictTo(RestrictTo.Scope.TESTS)
internal constructor(
@JsName("createTestButton")
constructor(
parent: Base = Manifest(),
style: Style? = null,
color: Color? = null,
Expand Down
25 changes: 25 additions & 0 deletions module/renderer/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
plugins {
id("godtools-shared.module-conventions")
alias(libs.plugins.compose)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.paparazzi)
}

android {
namespace = "org.cru.godtools.shared.renderer"
}

kotlin {
sourceSets {
val commonMain by getting {
dependencies {
api(project(":module:parser"))

implementation(compose.runtime)
implementation(compose.material3)

implementation(libs.colormath.jetpack.compose)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.cru.godtools.shared.renderer

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import app.cash.paparazzi.Paparazzi
import com.android.ide.common.rendering.api.SessionParams.RenderingMode
import org.junit.Rule

abstract class BasePaparazziTest(renderingMode: RenderingMode = RenderingMode.NORMAL) {
@get:Rule
val paparazzi = Paparazzi(
renderingMode = renderingMode,
maxPercentDifference = 0.0,
)

protected fun contentSnapshot(content: @Composable BoxScope.() -> Unit) {
paparazzi.snapshot {
Box(modifier = Modifier.background(Color.White), content = content)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.cru.godtools.shared.renderer.content

import com.android.ide.common.rendering.api.SessionParams.RenderingMode
import com.github.ajalt.colormath.model.RGB
import kotlin.test.Test
import org.cru.godtools.shared.renderer.BasePaparazziTest
import org.cru.godtools.shared.tool.parser.model.Button
import org.cru.godtools.shared.tool.parser.model.Text

class RenderButtonPaparazziTest : BasePaparazziTest(renderingMode = RenderingMode.SHRINK) {
@Test
fun `RenderButton() - Defaults`() = contentSnapshot {
RenderContentStack(
listOf(
Button(
style = Button.Style.CONTAINED,
text = { Text(it, "Default Contained Button") }
),
Button(
style = Button.Style.OUTLINED,
text = { Text(it, "Default Outlined Button") }
),
),
)
}

@Test
fun `RenderButton() - Color`() = contentSnapshot {
RenderContentStack(
listOf(
Button(
style = Button.Style.CONTAINED,
color = RGB(1, 0, 0, 1),
text = { Text(it, "Red Contained Button") }
),
Button(
style = Button.Style.OUTLINED,
color = RGB(1, 0, 0, 1),
text = { Text(it, "Red Outlined Button") }
),
Button(
style = Button.Style.CONTAINED,
color = RGB(0, 1, 0, 1),
text = { Text(it, "Green Contained Button") }
),
Button(
style = Button.Style.OUTLINED,
color = RGB(0, 1, 0, 1),
text = { Text(it, "Green Outlined Button") }
),
Button(
style = Button.Style.CONTAINED,
color = RGB(0, 0, 1, 1),
text = { Text(it, "Blue Contained Button") }
),
Button(
style = Button.Style.OUTLINED,
color = RGB(0, 0, 1, 1),
text = { Text(it, "Blue Outlined Button") }
),
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.cru.godtools.shared.renderer.content

import com.android.ide.common.rendering.api.SessionParams.RenderingMode
import com.github.ajalt.colormath.model.RGB
import kotlin.test.Test
import org.cru.godtools.shared.renderer.BasePaparazziTest
import org.cru.godtools.shared.tool.parser.model.Text

class RenderTextPaparazziTest : BasePaparazziTest(renderingMode = RenderingMode.SHRINK) {
@Test
fun `RenderText() - Simple`() = contentSnapshot {
RenderContentStack(listOf(Text(text = "Simple Text")))
}

@Test
fun `RenderText() - Color`() = contentSnapshot {
RenderContentStack(
listOf(
Text(text = "Default Color"),
Text(text = "Red Color", textColor = RGB(1, 0, 0, 1)),
Text(text = "Green Color", textColor = RGB(0, 1, 0, 1)),
Text(text = "Blue Color", textColor = RGB(0, 0, 1, 1)),
),
)
}

@Test
fun `RenderText() - Styles`() = contentSnapshot {
RenderContentStack(
listOf(
Text(text = "Italic Text", textStyles = setOf(Text.Style.ITALIC)),
Text(text = "Underline Text", textStyles = setOf(Text.Style.UNDERLINE)),
Text(text = "Italic Underline Text", textStyles = setOf(Text.Style.ITALIC, Text.Style.UNDERLINE)),
),
)
}

@Test
fun `RenderText() - Font Weight`() = contentSnapshot {
RenderContentStack(
listOf(
Text(text = "Font Weight 100", fontWeight = 100),
Text(text = "Font Weight 200", fontWeight = 200),
Text(text = "Font Weight 300", fontWeight = 300),
Text(text = "Font Weight 400", fontWeight = 400),
Text(text = "Font Weight 500", fontWeight = 500),
Text(text = "Font Weight 600", fontWeight = 600),
Text(text = "Font Weight 700", fontWeight = 700),
Text(text = "Font Weight 800", fontWeight = 800),
Text(text = "Font Weight 900", fontWeight = 900),
Text(text = "Font Weight 1000", fontWeight = 1000),
),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.cru.godtools.shared.renderer.content

import androidx.compose.foundation.BorderStroke
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import com.github.ajalt.colormath.extensions.android.composecolor.toComposeColor
import org.cru.godtools.shared.tool.parser.model.Button

@Composable
internal fun RenderButton(button: Button) {
Button(
onClick = { },
colors = when (button.style) {
Button.Style.OUTLINED -> ButtonDefaults.outlinedButtonColors(
containerColor = button.backgroundColor.toComposeColor()
)
Button.Style.CONTAINED, Button.Style.UNKNOWN -> ButtonDefaults.buttonColors(
containerColor = button.buttonColor.toComposeColor()
)
},
elevation = when (button.style) {
Button.Style.OUTLINED -> null
Button.Style.CONTAINED, Button.Style.UNKNOWN -> ButtonDefaults.buttonElevation()
},
border = when (button.style) {
Button.Style.OUTLINED -> BorderStroke(1.dp, button.buttonColor.toComposeColor())
Button.Style.CONTAINED, Button.Style.UNKNOWN -> null
}
) {
RenderText(button.text)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.cru.godtools.shared.renderer.content

import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import org.cru.godtools.shared.tool.parser.model.Button
import org.cru.godtools.shared.tool.parser.model.Content
import org.cru.godtools.shared.tool.parser.model.Paragraph
import org.cru.godtools.shared.tool.parser.model.Text

@Composable
internal fun ColumnScope.RenderContent(content: List<Content>) {
content.forEach { RenderContent(it) }
}

@Composable
internal fun ColumnScope.RenderContent(content: Content) {
when (content) {
is Text -> RenderText(content)
is Button -> RenderButton(content)
is Paragraph -> RenderParagraph(content)
else -> Text(
"Unsupported Content Element: ${content::class.simpleName}",
color = Color.Red,

Check warning on line 25 in module/renderer/src/commonMain/kotlin/org/cru/godtools/shared/renderer/content/RenderContent.kt

View check run for this annotation

Codecov / codecov/patch

module/renderer/src/commonMain/kotlin/org/cru/godtools/shared/renderer/content/RenderContent.kt#L24-L25

Added lines #L24 - L25 were not covered by tests
)
}
}
Loading