Skip to content
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

adding a new property into @ShowkaseComposable to generate or not the screenshot with Paparazzi integration #333

Open
IvanSanchez-uam opened this issue Jun 27, 2023 · 1 comment

Comments

@IvanSanchez-uam
Copy link

When we skip a preview function with @ShowkaseComposable(skip = true) this preview will not be included into the componentList : List<ShowkaseBrowserComponent> that is the expected behavior, but there is a case when I make the integration with Paparazzi where I don't want to show the preview in the componentBrowser but I want to generate the screenshot, right now this is not possible because as far as the preview was skipped there will not be metadata for it.

A component that is skipped will not be including in the next list

@ShowkaseRootCodegen(
  numComposablesWithoutPreviewParameter = 15,
  numComposablesWithPreviewParameter = 2,
  numColors = 7,
  numTypography = 13,
)
public class PaparazziSampleRootModuleCodegen : ShowkaseProvider {
  public override fun getShowkaseComponents(): List<ShowkaseBrowserComponent> {

    return mutableListOf<ShowkaseBrowserComponent>(
        // components that are skipped are not included here
        BasicChipPreviewChipsBasicChipDefaultStyle,
        BasicChipYellowPreviewChipsBasicChipYellowBackground,
        BottomLabelRowPreviewRowsBottomLabelRow,
        BottomNavigationAlwaysShowLabelComponentPreviewNavigationBottomNavigationBar,
       // more components
    ).apply {
        addAll(H6TextRowComponentPreviewTextH6TextRow)
    }
  }
}

I made changes in the repo locally and the solution was adding a new property into the @ShowkaseComposable annotation, I named the property generateScreenshot as I show next:

@MustBeDocumented
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.FUNCTION)
@Repeatable
@Suppress("LongParameterList")
annotation class ShowkaseComposable(
    val name: String = "",
    val group: String = "",
    val styleName: String = "",
    val widthDp: Int = -1,
    val heightDp: Int = -1,
    val skip: Boolean = false,
    val defaultStyle: Boolean = false,
    val tags: Array<String> = [],
    val extraMetadata: Array<String> = [],
    val generateScreenshot: Boolean = false,
)

and made changes where needed to use this property in the code. The usage of this property looks like this

@ShowkaseComposable(
    name = "Basic Chip",
    group = "Chips",
    defaultStyle = true,
    generateScreenshot = true,
    skip = true
)
@Composable
fun BasicChipPreview() {
    BasicChip(text = "Chip Component")
}

The generated file for this preview now includes the generateScreenshot property

public val BasicChipPreviewChipsBasicChipDefaultStyle: ShowkaseBrowserComponent =
    ShowkaseBrowserComponent(
        group = "Chips",
        componentName = "Basic Chip",
        functionName = "BasicChipPreview",
        componentKDoc = "",
        componentKey =
            """com.airbnb.android.showkase.screenshot.testing.paparazzi.sample_BasicChipPreview_null_Chips_BasicChip_0_DefaultStyle""",
        isDefaultStyle = true,
        generateScreenshot = true,
        styleName = "Default Style",
        component = @Composable { BasicChipPreview() }
    )

This property in general helps us to generate or not the screenshot with Paparazzi integration.

I have another need, as far as we can not handle the name of the screenshot too much with Paparazzi and It could be something short like the function name of the preview we can write the elementName into the metadata witch is already defined in Showkasemetadata.Component.

@Suppress("LongParameterList")
internal sealed class ShowkaseMetadata {
    abstract val element: XElement
    abstract val packageName: String
    abstract val packageSimpleName: String
    abstract val elementName: String
    abstract val showkaseName: String
    abstract val showkaseGroup: String
    abstract val showkaseKDoc: String
    abstract val enclosingClassName: ClassName?
    abstract val insideWrapperClass: Boolean
    abstract val insideObject: Boolean

    /** A fully qualified prefix for use when de-duplicating components. **/
    val fqPrefix: String
        get() = enclosingClassName?.let {"${it}_$elementName" } ?: "${packageName}_$elementName"

    data class Component(
        override val element: XElement,
        override val packageName: String,
        override val packageSimpleName: String,
        **override val elementName: String**,
        override val showkaseName: String,
        override val showkaseGroup: String,
        override val showkaseKDoc: String,
        override val enclosingClassName: ClassName? = null,
        override val insideWrapperClass: Boolean = false,
        override val insideObject: Boolean = false,
        val componentIndex: Int? = null,
        val showkaseWidthDp: Int? = null,
        val showkaseHeightDp: Int? = null,
        val previewParameterProviderType: TypeName? = null,
        val previewParameterName: String? = null,
        val showkaseStyleName: String? = null,
        val isDefaultStyle: Boolean = false,
        val tags: List<String> = emptyList(),
        val extraMetadata: List<String> = emptyList(),
        val showkaseGenerateScreenshot: Boolean = false
    ) : ShowkaseMetadata()

and write the property into the CodeBlock.Builder.addShowkaseBrowserComponent() method of the class WriterUtils.kt like this

internal fun CodeBlock.Builder.addShowkaseBrowserComponent(
    showkaseMetadata: ShowkaseMetadata.Component,
    isPreviewParameter: Boolean = false
) {
    val componentName = if (showkaseMetadata.componentIndex != null) {
        "_${showkaseMetadata.showkaseName}_${showkaseMetadata.componentIndex}"
    } else {
        "_${showkaseMetadata.showkaseName}"
    }
    var componentKey = (showkaseMetadata.fqPrefix +
            "_${showkaseMetadata.enclosingClassName}" +
            "_${showkaseMetadata.showkaseGroup}" +
            componentName +
            "_${showkaseMetadata.showkaseStyleName}").replace(
        SPACE_REGEX,
        ""
    )
    if (isPreviewParameter) {
        componentKey += "_\$index"
    }
    add(
        "%T(\n",
        ShowkaseBrowserWriter.SHOWKASE_BROWSER_COMPONENT_CLASS_NAME
    )
    doubleIndent()
    add(
        "group = %S,\ncomponentName = %S,\n**elementName** = %S,\ncomponentKDoc = %S,\ncomponentKey = %P,",
        showkaseMetadata.showkaseGroup,
        showkaseMetadata.showkaseName,
        **showkaseMetadata.elementName**,
        showkaseMetadata.showkaseKDoc,
        componentKey,
    )
    // more content
}

and the result of this will look like this in the generated file

public val BasicChipPreviewChipsBasicChipDefaultStyle: ShowkaseBrowserComponent =
    ShowkaseBrowserComponent(
        group = "Chips",
        componentName = "Basic Chip",
        **elementName = "BasicChipPreview"**,
        componentKDoc = "",
        componentKey =
            """com.airbnb.android.showkase.screenshot.testing.paparazzi.sample_BasicChipPreview_null_Chips_BasicChip_0_DefaultStyle""",
        isDefaultStyle = true,
        **generateScreenshot = true**,
        styleName = "Default Style",
        component = @Composable { BasicChipPreview() }
    )

I have the complete implementation, let me know if you want to see it and I can share it. Hope this make sense to you as well.

@vinaygaba
Copy link
Collaborator

@IvanSanchez-uam Could you open a PR up so that I can see what this looks like. Early thoughts - I'm not a fan of adding more parameters in the annotation and instead want to explore other ways to handle this. Open to ideas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants