Skip to content

feat(list-item): update component to version 0.3 adding custom contents, badge and tag as trailing elements - #1603

Open
paulinea wants to merge 17 commits into
developfrom
1401-in-library-add-list-item-component-v02
Open

paulinea wants to merge 17 commits into
developfrom
1401-in-library-add-list-item-component-v02

Conversation

@paulinea

Copy link
Copy Markdown
Member

No description provided.

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

helperText: String? = null,
boldLabel: Boolean = false,
@IntroducedAt("2.3-Unreleased") belowTextContent: @Composable (OudsListItemScope.() -> Unit)? = null,
@IntroducedAt("2.3-Unreleased") bottomContent: @Composable (OudsListItemScope.() -> Unit)? = null,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding new parameters here instead of the last position might break source compatibility. This is highly unlikely because this would only happen if a user called this method without any parameter name.

Until now we added new parameters at the end to ensure we don't have any source compatibility issue, but we can decide to do differently if you want.

Here is what the official guidelines says:

Since every compose is a function, the following rules apply to the component API changes:

  • Parameters of the functions MUST NOT be removed.
  • Newly added parameter to existing functions MUST have default expressions.
  • New parameters MAY be added as a last parameter, or second to last in cases of trailing lambdas.
  • The developer might decide to put the new parameter closer to other parameters that are semantically close to a new one. Keep in mind that this might break source compatibility if the user uses the component without named parameters.

https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md#evolution-of-the-component-apis

Same comment for OudsCardItem.

background: Boolean = false,
helperText: String? = null,
boldLabel: Boolean = false,
@IntroducedAt("2.3-Unreleased") belowTextContent: @Composable (OudsListItemScope.() -> Unit)? = null,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a detail and I am not even sure you need to change this because it does not have any impact and will be updated by the prepareRelease task, but we should write @IntroducedAt("2.3.0-Unreleased").


Column(
modifier = modifier.sizeIn(minWidth = this.size.minWidth)
modifier = modifier.sizeIn(minHeight = minHeight(size), minWidth = this.size.minWidth)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Min height is also applied to the Row below (this is also the case in Figma). We can keep it to match Figma but it should have no effect on the Column.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we always had this case for another component but I don't remember which one and what we decided.

}

@Composable
private fun CustomContentBox(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this composable does not have Box parameter (such as contentAlignment or propagateMinConstraints), what do you think or renaming it to CustomContent (or ListItemContent)?


interface Badge : OudsListItemLeadingTrailing {
@ConsistentCopyVisibility
data class ExtraParameters internal constructor(internal val state: OudsListItemState) : OudsComponentContent.ExtraParameters()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In OudsTagAsset and OudsBulletListUnorderedAsset which also are OudsPolymorphicComponentContent, we only declare a single ExtraParameters class that contains all properties. Some properties are sometimes useless (depending on the cases) but this simplifies the code here and where we call the PolymorphicContent method.

Then the following code:

trailing?.let {
    when (trailing) {
        is OudsListItemLeadingTrailing.Badge -> trailing.PolymorphicContent(
            extraParameters = OudsListItemLeadingTrailing.Badge.ExtraParameters(state = state)
        )
        is OudsListItemLeadingTrailing.Content -> trailing.PolymorphicContent(
            extraParameters = OudsListItemLeadingTrailing.Content.ExtraParameters(state = state)
        )
        is OudsListItemLeadingTrailing.Icon -> trailing.PolymorphicContent(
            extraParameters = OudsListItemLeadingTrailing.Icon.ExtraParameters(state = state)
        )
        is OudsListItemLeadingTrailing.Image -> trailing.PolymorphicContent()
        is OudsListItemLeadingTrailing.Tag -> trailing.PolymorphicContent(
            extraParameters = OudsListItemLeadingTrailing.Tag.ExtraParameters(state = state)
        )
        is OudsListItemLeadingTrailing.Text -> {
            trailing.PolymorphicContent(
                extraParameters = OudsListItemLeadingTrailing.Text.ExtraParameters(
                    state = state,
                    verticalAlignment = verticalAlignment,
                    size = size
                )
            )
        }
    }
}

could be simplified to this:

trailing?.PolymorphicContent(
    extraParameters = OudsListItemLeadingTrailing.ExtraParameters(
        state = state,
        verticalAlignment = verticalAlignment,
        size = size
    )
)

However, I am not sure because the dump does not seems to change but I wonder if this might break binary compatibility because for instance OudsListItemBadge which inherits from OudsComponentContent<OudsListItemLeadingTrailing.Badge.ExtraParameters> will now inherit from OudsComponentContent<OudsListItemLeadingTrailing.ExtraParameters>.

We can discuss that point after everything else is OK if you want, because I don't know if it's worth it.

OudsSmallListItemTrailing.Badge(
count = TrailingBadgeExampleCount,
status = trailingBadgeExampleStatus,
enabled = enabled

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed because the badge will inherit the enabled state of the list item.

Same comment for the tag.

If we fix this, this will also impact the code snippet.

null
}

val baseLabelContent: @Composable OudsListItemScope.() -> Unit = { CustomContent(state = this.state, modifier = Modifier.fillMaxWidth()) }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was looking for a way to factorize calls to CustomContent and I found this solution:

val baseLabelContent: @Composable OudsListItemScope.() -> Unit = requireNotNull(getContentOrNull(true))

or this one:

fun getContentOrNull(contentActivated: Boolean): @Composable (OudsListItemScope.() -> Unit)? = if (contentActivated) getContent() else null

fun getContent(): @Composable (OudsListItemScope.() -> Unit) = { CustomContent(state = state, modifier = Modifier.fillMaxWidth()) }

where getContent would replace baseLabelContent.

By the way, maybe getBaseListItemContent would be more appropriate because this is a top level function.


@Composable
fun CustomContent(state: OudsListItemState, modifier: Modifier = Modifier) {
val enabled = state == OudsListItemState.Enabled

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be:

val enabled = state != OudsListItemState.Disabled

Also, that's very minor, but this method could be private.


enum class Leading {
None, Icon, Image
enum class Leading(val availableForSizes: List<Size> = listOf(Size.Default, Size.Small)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For other components, we chose to disabled the entries rather than hiding them. For instance for the FAB:

val enabledLayouts: List<Layout>
    get() = when (size) {
        Size.Small,
        Size.Large -> listOf(Layout.IconOnly)
        Size.Medium -> Layout.entries
    }

I don't know if we must follow the same pattern here or if we should hide them as you did.

<string name="app_components_listItem_specificComponentExampleAlertLabel_label">مثال %1$s</string>
<string name="app_components_listItem_componentExampleAlertLabel_label">مثال على مكوّن</string>
<string name="app_components_listItem_componentExampleAlertDescription_text">يرجى الاطلاع على إعدادات المكون المحددة للحصول على خيارات التكوين الكاملة.</string>
<string name="app_components_listItem_contentExempleAlertDescription_text">استخدم عنصر "Content" للمحتوى المخصص الذي لا يمكن تمثيله بواسطة أي عنصر آخر موجود؛ إذ يوفر هذا العنصر مرونةً لتلبية متطلبات محددة للمنتج، ولكن ينبغي استخدامه كاستثناء وليس كحلٍ افتراضي.</string>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about renaming Example to Settings?

Also, there is a typo here, this should be Example instead of Exemple.

This branch was successfully deployed

1 active deployment
netlify — 6127a777 Deployed Oct 20, 2026 by florentmaitre via netlify #5744
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

Successfully merging this pull request may close these issues.

In library, add list item component v0.3 In library, add list item component v0.2

2 participants