Enable screenshots of animated components#421
Conversation
This adds support for capturing animated PNGs of components annotated with `@ShowkaseComposable`, by specifying the new `screenshotConfig` parameter. For example, to capture an animation for 2 seconds with a framerate of 60fps:
```kotlin
@ShowkaseComposable(
name = "MyAnimatedComponent",
group = "AnimatedGroup",
defaultStyle = true,
screenshotCaptureType = ScreenshotCaptureType.SingleAnimatedImage,
captureDurationMillis = 2000,
captureFramerate = 60,
)
@composable
fun MyAnimatedComponentPreview_Default() {
var animatedComponentState by remember { mutableStateOf(...) }
LaunchedEffect(Unit) {
animatedComponentState = ...
}
MyAnimatedComponent(animatedComponentState)
}
```
Updates Paparazzi to `2.0.0-alpha02` to fix [this issue](cashapp/paparazzi#1877) with compileSdk 36.
| testClassName: String | ||
| ) { | ||
| val showkaseScreenshotTestClassName = "${testClassName}_PaparazziShowkaseTest" | ||
| val showkaseScreenshotTestClassName = "${testClassName}Impl" |
There was a problem hiding this comment.
Note: This contains the fixes in #419 to shorten the generated Paparazzi image filenames - due to changes in Paparazzi, we can now encounter java.io.FileNotFoundException: File name too long on some systems.
| /** | ||
| * Configuration for how screenshots of the annotated Composable should be captured. | ||
| */ | ||
| sealed interface ScreenshotConfig { |
| * Used by Paparazzi snapshot testing to determine if the component has any animation, and how to capture | ||
| * the screenshot. | ||
| */ | ||
| val screenshotCaptureType: ScreenshotCaptureType = ScreenshotCaptureType.SingleStaticImage, |
There was a problem hiding this comment.
Wish there was a cleaner way (with a single annotation param) to do this so that it was more encapsulated. I think Arrays are the only choice but that's ugly.
There was a problem hiding this comment.
Yeah unfortunately we're limited by what annotation classes support, hence the need to convert it to ScreenshotConfig later down the line.
There was a problem hiding this comment.
@allenchen1154 can you use nested annotations to encapsulate all animation related properties?
eg
annotation class Foo(val bar: Bar)
annotation class Bar(val x: Int)
@Foo(bar = Bar(x = 1))
class Baz
There was a problem hiding this comment.
What @elihart is suggesting would be really neat!
There was a problem hiding this comment.
@elihart thanks for the suggestion, moved these params into a wrapper annotation
|
What's the current behavior when it comes to the Showkase browser itself? How are those preview rendered in the browser? Additionally, if you are using offsets, should they show up as separate previews? I think that would be the ideal behavior but since you are using Paparazzi for the screenshots, I suspect that might not be possible without some additional work as the browser shows live composable instead. |
|
In an ideal case, we maintain parity between what you see in the browser and what gets screenshot tested. |
|
@vinaygaba Currently the previews that show in the Showkase browser would just play the animation as it's specified by the preview Composable. So in the example I added, it would simply animate the red square to the right one time. You're right that generating separate browser components for the offsets would require more work/codegen. I think having this discrepancy is OK for now, as I'm hoping the APNG approach will be preferred. |
|
@vinaygaba (The offset approach doesn't actually work right now due to a bug in Paparazzi: cashapp/paparazzi#1645) |
This was causing an error in the compilation tests when running in KAPT mode.
vinaygaba
left a comment
There was a problem hiding this comment.
Changes LGTM. Would be nice to add test coverage - processor tests especially
This test checks that when `screenshotCaptureType` is provided, the generated`ShowkaseBrowserComponent` instances have the correct `ScreenshotConfig` values.
|
@vinaygaba I added a processor test, will merge and prepare a 1.1.0 release. |
Includes changes from #421 back-ported to work on older version of XProcessing library, plus necessary changes to use newer Paparazzi
This adds support for capturing animated PNGs of components annotated with
@ShowkaseComposable, by specifying the newscreenshotCaptureTypeparameter. For example, to capture an animation for 2 seconds with a framerate of 60fps:@ShowkaseComposable( name = "MyAnimatedComponent", group = "AnimatedGroup", defaultStyle = true, screenshotCaptureType = ScreenshotCaptureType.SingleAnimatedImage, captureDurationMillis = 2000, captureFramerate = 60, ) @Composable fun MyAnimatedComponentPreview_Default() { var animatedComponentState by remember { mutableStateOf(...) } LaunchedEffect(Unit) { animatedComponentState = ... } MyAnimatedComponent(animatedComponentState) }Updates Paparazzi to
2.0.0-alpha02to fix this issue with compileSdk 36.Screen.Recording.2025-09-05.at.21.19.47.mov