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

feat: create button component #213

Merged
merged 27 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
68aa8d1
chore: update tokens
boosted-bot Dec 12, 2024
29966ac
Update code to token keys and components tokens generation by Tokenator
paulinea Dec 12, 2024
48fc9af
Update OudsButton according to design specifications
florentmaitre Nov 27, 2024
abf62b6
Add components navigation system
paulinea Nov 26, 2024
932759b
Add button demo screen with its customization options
paulinea Nov 27, 2024
ea58b04
Remove OnResumeEffect and use LifecycleResumeEffect instead
florentmaitre Nov 28, 2024
de88bcb
Remove ComponentDetailScreen
florentmaitre Nov 28, 2024
8fd945f
Update OudsButton demo with the actual button
florentmaitre Nov 29, 2024
8579ce4
OudsButton now supports both determinate and indeterminate circular p…
florentmaitre Nov 29, 2024
b85be8b
Review: Rename parameters of private OudsButton method
florentmaitre Dec 3, 2024
d5e7221
Review: Use a list instead of multiple conditions
florentmaitre Dec 3, 2024
ace3917
Review: Factorize skeleton color
florentmaitre Dec 3, 2024
fadf7ee
Review: Remove useless val
florentmaitre Dec 3, 2024
0415fc2
Review: Fix KDoc for OudsButton.Style.Skeleton
florentmaitre Dec 3, 2024
8aebf0c
Review: Button demo is now displayed for both light and dark backgrou…
florentmaitre Dec 3, 2024
8b6a9a1
Review: Fix a bug where text was read by TalkBack on a skeleton button
florentmaitre Dec 3, 2024
d9fe970
Add a11y state description for loading button
florentmaitre Dec 3, 2024
dbcdec8
Review: Add contentDescription parameter to OudsButton.Icon constructors
florentmaitre Dec 3, 2024
9ce6264
Review: Remove useless extension method on Color
florentmaitre Dec 3, 2024
71ee6bd
Review: Move OudsIconButtonDefaults into OudsIconButton.kt and OudsIc…
florentmaitre Dec 3, 2024
a91109d
Review: Add inner border for focus state
florentmaitre Dec 3, 2024
739074a
Review: Fix KDoc for outerBorder method
florentmaitre Dec 3, 2024
11e5a11
Review: Remove useless enabled parameter and OudsIconButton
florentmaitre Dec 4, 2024
05600b6
Review: Fix a bug where TalkBack vocalizes the button loader
florentmaitre Dec 4, 2024
5855e10
Add missing copyright
paulinea Dec 12, 2024
e11ce69
Fix issues after rebase
paulinea Dec 18, 2024
ee852a7
Update paparazzi snapshots
paulinea Dec 18, 2024
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
6 changes: 6 additions & 0 deletions NOTICE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ app/src/main/res/drawable/ic_copy.xml
app/src/main/res/drawable/ic_design_token_figma.xml
app/src/main/res/drawable/ic_dimension.xml
app/src/main/res/drawable/ic_filter_effects.xml
app/src/main/res/drawable/ic_heart.xml
app/src/main/res/drawable/ic_info.xml
app/src/main/res/drawable/ic_layers.xml
app/src/main/res/drawable/ic_menu_grid.xml
Expand All @@ -28,18 +29,23 @@ app/src/main/res/drawable/ic_typography.xml
app/src/main/res/drawable/ic_ui_dark_mode.xml
app/src/main/res/drawable/ic_ui_light_mode.xml
app/src/main/res/drawable/il_opacity_union.xml
app/src/main/res/drawable-hdpi/il_components_button.png
app/src/main/res/drawable-hdpi/il_tokens_grid_column_margin.png
app/src/main/res/drawable-hdpi/il_tokens_grid_max_width.png
app/src/main/res/drawable-hdpi/il_tokens_grid_min_width.png
app/src/main/res/drawable-mdpi/il_components_button.png
app/src/main/res/drawable-mdpi/il_tokens_grid_column_margin.png
app/src/main/res/drawable-mdpi/il_tokens_grid_max_width.png
app/src/main/res/drawable-mdpi/il_tokens_grid_min_width.png
app/src/main/res/drawable-xhdpi/il_components_button.png
app/src/main/res/drawable-xhdpi/il_tokens_grid_column_margin.png
app/src/main/res/drawable-xhdpi/il_tokens_grid_max_width.png
app/src/main/res/drawable-xhdpi/il_tokens_grid_min_width.png
app/src/main/res/drawable-xxhdpi/il_components_button.png
app/src/main/res/drawable-xxhdpi/il_tokens_grid_column_margin.png
app/src/main/res/drawable-xxhdpi/il_tokens_grid_max_width.png
app/src/main/res/drawable-xxhdpi/il_tokens_grid_min_width.png
app/src/main/res/drawable-xxxhdpi/il_components_button.png
app/src/main/res/drawable-xxxhdpi/il_tokens_grid_column_margin.png
app/src/main/res/drawable-xxxhdpi/il_tokens_grid_max_width.png
app/src/main/res/drawable-xxxhdpi/il_tokens_grid_min_width.png
Expand Down
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ plugins {
id(libs.plugins.android.application.get().pluginId) // https://github.com/gradle/gradle/issues/20084#issuecomment-1060822638
id(libs.plugins.kotlin.android.get().pluginId)
id(libs.plugins.kotlin.kapt.get().pluginId)
id(libs.plugins.kotlin.parcelize.get().pluginId)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.firebase.appdistribution)
alias(libs.plugins.firebase.crashlytics)
Expand Down
20 changes: 12 additions & 8 deletions app/src/main/java/com/orange/ouds/app/ui/Screen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import com.orange.ouds.app.R
import com.orange.ouds.app.ui.about.AboutDestinations
import com.orange.ouds.app.ui.about.AboutMenuItem
import com.orange.ouds.app.ui.about.AboutNavigationKey
import com.orange.ouds.app.ui.components.Component
import com.orange.ouds.app.ui.components.ComponentsNavigation
import com.orange.ouds.app.ui.tokens.TokenCategory
import com.orange.ouds.app.ui.tokens.TokensNavigation
import com.orange.ouds.foundation.UiString
Expand All @@ -33,14 +35,9 @@ fun getScreen(route: String, args: Bundle?): Screen? {
// Specific element route -> get element id
val (routeRoot) = matchElementRouteResult.destructured
when (routeRoot) {
TokensNavigation.TokenCategoryDetailRoute -> {
args?.getLong(TokensNavigation.TokenCategoryIdKey)?.let { Screen.TokenCategoryDetail(it) }
}

AboutDestinations.FileRoute -> {
args?.getLong(AboutNavigationKey.MenuItemIdKey)?.let { Screen.AboutFile(it) }
}

TokensNavigation.TokenCategoryDetailRoute -> args?.getLong(TokensNavigation.TokenCategoryIdKey)?.let { Screen.TokenCategoryDetail(it) }
ComponentsNavigation.ComponentDetailRoute -> args?.getLong(ComponentsNavigation.ComponentIdKey)?.let { Screen.ComponentDetail(it) }
AboutDestinations.FileRoute -> args?.getLong(AboutNavigationKey.MenuItemIdKey)?.let { Screen.AboutFile(it) }
else -> null
}
} else {
Expand Down Expand Up @@ -94,6 +91,13 @@ sealed class Screen(
title = TokenCategory.fromId(tokenCategoryId)?.nameRes?.let { UiString.StringResource(it) }
)

// Components screens

data class ComponentDetail(val componentId: Long) : Screen(
route = ComponentsNavigation.ComponentDetailRoute,
title = Component.fromId(componentId)?.nameRes?.let { UiString.StringResource(it) }
)

// About screens

data class AboutFile(val menuItemId: Long) : Screen(
Expand Down
44 changes: 44 additions & 0 deletions app/src/main/java/com/orange/ouds/app/ui/components/Component.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Software Name: OUDS Android
* SPDX-FileCopyrightText: Copyright (c) Orange SA
* SPDX-License-Identifier: MIT
*
* This software is distributed under the MIT license,
* the text of which is available at https://opensource.org/license/MIT/
* or see the "LICENSE" file for more details.
*
* Software description: Android library of reusable graphical components
*/

package com.orange.ouds.app.ui.components

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import com.orange.ouds.app.R
import com.orange.ouds.app.ui.components.button.ButtonDemoScreen

val components = Component::class.sealedSubclasses.mapNotNull { it.objectInstance }

@Immutable
sealed class Component(
@StringRes val nameRes: Int,
@DrawableRes val imageRes: Int,
@StringRes val descriptionRes: Int,
val demoScreen: @Composable () -> Unit
) {

companion object {
fun fromId(componentId: Long) = components.firstOrNull { component -> component.id == componentId }
}

val id: Long = Component::class.sealedSubclasses.indexOf(this::class).toLong()

data object Button : Component(
R.string.app_components_button_label,
R.drawable.il_components_button,
R.string.app_components_button_description_text,
{ ButtonDemoScreen() }
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Software Name: OUDS Android
* SPDX-FileCopyrightText: Copyright (c) Orange SA
* SPDX-License-Identifier: MIT
*
* This software is distributed under the MIT license,
* the text of which is available at https://opensource.org/license/MIT/
* or see the "LICENSE" file for more details.
*
* Software description: Android library of reusable graphical components
*/

package com.orange.ouds.app.ui.components

import androidx.compose.runtime.remember
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavType
import androidx.navigation.compose.composable
import androidx.navigation.navArgument

object ComponentsNavigation {
const val ComponentDetailRoute = "component"
const val ComponentIdKey = "componentId"
}

fun NavGraphBuilder.addComponentsNavGraph() {
composable(
"${ComponentsNavigation.ComponentDetailRoute}/{${ComponentsNavigation.ComponentIdKey}}",
arguments = listOf(navArgument(ComponentsNavigation.ComponentIdKey) { type = NavType.LongType })
) { from ->
val arguments = requireNotNull(from.arguments)
val routeComponentId = arguments.getLong(ComponentsNavigation.ComponentIdKey)

val component = remember(routeComponentId) { Component.fromId(routeComponentId) }
component?.let {
component.demoScreen()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,53 +12,55 @@

package com.orange.ouds.app.ui.components

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.res.stringResource
import com.orange.ouds.app.ui.utilities.composable.LargeCard
import com.orange.ouds.app.ui.utilities.composable.Screen
import com.orange.ouds.core.component.button.OudsButton
import com.orange.ouds.core.theme.value
import com.orange.ouds.core.utilities.OudsPreview
import com.orange.ouds.foundation.utilities.UiModePreviews
import com.orange.ouds.theme.tokens.OudsColorKeyToken
import com.orange.ouds.theme.tokens.OudsGridKeyToken
import com.orange.ouds.theme.tokens.OudsSpaceKeyToken

@Composable
fun ComponentsScreen() {
fun ComponentsScreen(onComponentClick: (Long) -> Unit) {
ComponentsScreen(
components = components,
onComponentClick = onComponentClick
)
}

@Composable
private fun ComponentsScreen(components: List<Component>, onComponentClick: (Long) -> Unit) {
Screen {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(OudsSpaceKeyToken.Fixed.Medium.value),
verticalArrangement = Arrangement.spacedBy(OudsSpaceKeyToken.Fixed.Medium.value)
) {
Text(modifier = Modifier.padding(bottom = 8.dp), text = "Components screen")

OudsButton(text = "OUDS button", onClick = { })

Box(
modifier = Modifier
.padding(top = OudsSpaceKeyToken.Fixed.Medium.value)
.width(OudsGridKeyToken.Margin.value)
.height(OudsGridKeyToken.ColumnGap.value)
.background(OudsColorKeyToken.Content.BrandPrimary.value)
)
components.forEach { component ->
LargeCard(
title = stringResource(id = component.nameRes),
imageRes = component.imageRes,
onClick = { onComponentClick(component.id) }
)
}
}
}
}

@UiModePreviews.Default
@Composable
private fun PreviewComponentsScreen() = OudsPreview {
ComponentsScreen()
ComponentsScreen(
components = listOf(Component.Button)
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Software Name: OUDS Android
* SPDX-FileCopyrightText: Copyright (c) Orange SA
* SPDX-License-Identifier: MIT
*
* This software is distributed under the MIT license,
* the text of which is available at https://opensource.org/license/MIT/
* or see the "LICENSE" file for more details.
*
* Software description: Android library of reusable graphical components
*/

package com.orange.ouds.app.ui.components.button

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import com.orange.ouds.app.R
import com.orange.ouds.app.ui.components.Component
import com.orange.ouds.app.ui.utilities.composable.CustomizationBottomSheetScaffold
import com.orange.ouds.app.ui.utilities.composable.CustomizationChoiceChipsColumn
import com.orange.ouds.app.ui.utilities.composable.CustomizationSwitchListItem
import com.orange.ouds.app.ui.utilities.composable.DemoScreen
import com.orange.ouds.app.ui.utilities.composable.DetailScreenDescription
import com.orange.ouds.core.component.button.OudsButton
import com.orange.ouds.core.theme.OudsTheme
import com.orange.ouds.core.theme.OudsThemeTweak
import com.orange.ouds.core.theme.value
import com.orange.ouds.core.utilities.OudsPreview
import com.orange.ouds.foundation.utilities.UiModePreviews
import com.orange.ouds.theme.tokens.OudsSpaceKeyToken


@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ButtonDemoScreen() = DemoScreen(rememberButtonDemoState()) {
CustomizationBottomSheetScaffold(
bottomSheetScaffoldState = rememberBottomSheetScaffoldState(),
bottomSheetContent = {
CustomizationSwitchListItem(
label = stringResource(R.string.app_common_enabled_label),
checked = enabled,
onCheckedChange = { enabled = it },
enabled = style == OudsButton.Style.Default
)
CustomizationChoiceChipsColumn(
modifier = Modifier.padding(top = OudsSpaceKeyToken.Fixed.Medium.value),
label = stringResource(R.string.app_components_button_hierarchy_label),
chipsLabels = OudsButton.Hierarchy.entries.map { it.name },
selectedChipIndex = OudsButton.Hierarchy.entries.indexOf(hierarchy),
onSelectionChange = { id -> hierarchy = OudsButton.Hierarchy.entries[id] }
)
val styles = remember {
listOf(
OudsButton.Style.Default,
OudsButton.Style.Loading(progress = null),
OudsButton.Style.Skeleton
)
}
CustomizationChoiceChipsColumn(
modifier = Modifier.padding(top = OudsSpaceKeyToken.Fixed.Medium.value),
label = stringResource(R.string.app_components_button_style_label),
chipsLabels = styles.map { it::class.simpleName.orEmpty() },
selectedChipIndex = styles.indexOf(style),
onSelectionChange = { id -> style = styles[id] }
)
CustomizationChoiceChipsColumn(
modifier = Modifier.padding(top = OudsSpaceKeyToken.Fixed.Medium.value),
label = stringResource(R.string.app_components_button_layout_label),
chipsLabels = ButtonDemoState.Layout.entries.map { stringResource(it.labelRes) },
selectedChipIndex = ButtonDemoState.Layout.entries.indexOf(layout),
onSelectionChange = { id -> layout = ButtonDemoState.Layout.entries[id] }
)
}
) {
Column(modifier = Modifier.fillMaxWidth()) {
DetailScreenDescription(
modifier = Modifier.padding(all = OudsSpaceKeyToken.Fixed.Medium.value),
descriptionRes = Component.Button.descriptionRes
)
ButtonDemo(state = this@DemoScreen)
OudsThemeTweak(OudsTheme.Tweak.Invert) {
ButtonDemo(state = this@DemoScreen)
}
}
}
}

@Composable
private fun ButtonDemo(state: ButtonDemoState) {
Box(
modifier = Modifier
.background(OudsTheme.colorScheme.backgroundColors.primary)
.padding(all = OudsSpaceKeyToken.Fixed.Medium.value)
.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
val text = stringResource(id = R.string.app_components_button_label)
val icon = OudsButton.Icon(painterResource(id = R.drawable.ic_heart), stringResource(id = R.string.app_components_button_icon_a11y))
with(state) {
when (layout) {
ButtonDemoState.Layout.TextOnly -> {
OudsButton(
text = text,
onClick = {},
enabled = enabled,
style = style,
hierarchy = hierarchy
)
}
ButtonDemoState.Layout.IconAndText -> {
OudsButton(
icon = icon,
text = text,
onClick = {},
enabled = enabled,
style = style,
hierarchy = hierarchy
)
}
ButtonDemoState.Layout.IconOnly -> {
OudsButton(
icon = icon,
onClick = {},
enabled = enabled,
style = style,
hierarchy = hierarchy
)
}
}
paulinea marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

@UiModePreviews.Default
@Composable
private fun PreviewButtonDemoScreen() = OudsPreview {
ButtonDemoScreen()
}
Loading
Loading