Skip to content

Commit bf41830

Browse files
committed
Add SuggestionChip
1 parent a6ffbe1 commit bf41830

12 files changed

Lines changed: 329 additions & 25 deletions
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Software Name: OUDS Android
3+
* SPDX-FileCopyrightText: Copyright (c) Orange SA
4+
* SPDX-License-Identifier: MIT
5+
*
6+
* This software is distributed under the MIT license,
7+
* the text of which is available at https://opensource.org/license/MIT/
8+
* or see the "LICENSE" file for more details.
9+
*
10+
* Software description: Android library of reusable graphical components
11+
*/
12+
13+
package com.orange.ouds.core.component
14+
15+
import androidx.compose.ui.Modifier
16+
import androidx.compose.ui.platform.testTag
17+
import androidx.compose.ui.test.junit4.createComposeRule
18+
import androidx.compose.ui.test.onNodeWithTag
19+
import androidx.compose.ui.test.performClick
20+
import com.orange.ouds.core.extension.setOudsContent
21+
import org.junit.Rule
22+
import org.junit.Test
23+
import org.mockito.kotlin.mock
24+
import org.mockito.kotlin.verify
25+
26+
internal class OudsSuggestionChipTest {
27+
28+
@get:Rule
29+
val composeTestRule = createComposeRule()
30+
31+
@Test
32+
fun oudsSuggestionChip_click_succeeds() {
33+
with(composeTestRule) {
34+
val testTag = "OudsSuggestionChip"
35+
val onClick = mock<() -> Unit>()
36+
37+
setOudsContent {
38+
OudsSuggestionChip(
39+
onClick = onClick,
40+
label = "Label",
41+
modifier = Modifier.testTag(testTag)
42+
)
43+
}
44+
45+
onNodeWithTag(testTag).performClick()
46+
verify(onClick).invoke()
47+
}
48+
}
49+
}

core/src/main/java/com/orange/ouds/core/component/OudsChip.kt

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ package com.orange.ouds.core.component
1414

1515
import androidx.compose.foundation.background
1616
import androidx.compose.foundation.border
17+
import androidx.compose.foundation.clickable
1718
import androidx.compose.foundation.interaction.MutableInteractionSource
1819
import androidx.compose.foundation.layout.Arrangement
1920
import androidx.compose.foundation.layout.Box
@@ -58,17 +59,17 @@ import com.orange.ouds.foundation.extensions.orElse
5859

5960
@Composable
6061
internal fun OudsChip(
62+
selectable: Boolean,
6163
selected: Boolean,
6264
onClick: () -> Unit,
63-
nullableLabel: String?,
64-
nullableIcon: OudsChip.Icon?,
65+
label: String?,
66+
icon: OudsChip.Icon?,
67+
iconPosition: OudsChip.IconPosition,
6568
modifier: Modifier = Modifier,
6669
enabled: Boolean = true,
6770
interactionSource: MutableInteractionSource? = null
6871
) {
69-
val label = nullableLabel
70-
val icon = nullableIcon
71-
72+
val selected = selectable && selected
7273
val chipTokens = OudsTheme.componentsTokens.chip
7374
@Suppress("NAME_SHADOWING") val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
7475
val interactionState by interactionSource.collectInteractionStateAsState()
@@ -119,14 +120,28 @@ internal fun OudsChip(
119120
}
120121
.outerBorder(state = state, shape = shape)
121122
.padding(paddingValues = contentPadding(label, icon))
122-
.selectable(
123-
selected = selected,
124-
enabled = state != OudsChip.State.Disabled,
125-
interactionSource = interactionSource,
126-
indication = InteractionValuesIndication(contentColor, tickColor, backgroundColor, borderColor, borderWidth),
127-
onClick = onClick,
128-
role = Role.Checkbox
129-
),
123+
.run {
124+
val enabled = state != OudsChip.State.Disabled
125+
val indication = InteractionValuesIndication(contentColor, tickColor, backgroundColor, borderColor, borderWidth)
126+
if (selectable) {
127+
selectable(
128+
selected = selected,
129+
enabled = enabled,
130+
interactionSource = interactionSource,
131+
indication = indication,
132+
onClick = onClick,
133+
role = Role.Checkbox
134+
)
135+
} else {
136+
clickable(
137+
enabled = enabled,
138+
interactionSource = interactionSource,
139+
indication = indication,
140+
onClick = onClick,
141+
role = Role.Button
142+
)
143+
}
144+
},
130145
horizontalArrangement = Arrangement.spacedBy(chipTokens.spaceColumnGapIcon.value, Alignment.CenterHorizontally),
131146
verticalAlignment = Alignment.CenterVertically
132147
) {
@@ -137,6 +152,21 @@ internal fun OudsChip(
137152
contentDescription = null
138153
)
139154
}
155+
156+
val icon: @Composable () -> Unit = {
157+
icon?.Content(
158+
modifier = Modifier
159+
.size(chipTokens.sizeIcon.value * iconScale)
160+
.semantics {
161+
contentDescription = if (label == null) icon.contentDescription else ""
162+
},
163+
extraParameters = OudsChip.Icon.ExtraParameters(tint = contentColor.value)
164+
)
165+
}
166+
167+
if (iconPosition == OudsChip.IconPosition.Start) {
168+
icon()
169+
}
140170
if (label != null) {
141171
Text(
142172
modifier = modifier,
@@ -146,14 +176,9 @@ internal fun OudsChip(
146176
textAlign = TextAlign.Center
147177
)
148178
}
149-
icon?.Content(
150-
modifier = Modifier
151-
.size(chipTokens.sizeIcon.value * iconScale)
152-
.semantics {
153-
contentDescription = if (label == null) icon.contentDescription else ""
154-
},
155-
extraParameters = OudsChip.Icon.ExtraParameters(tint = contentColor.value)
156-
)
179+
if (iconPosition == OudsChip.IconPosition.End) {
180+
icon()
181+
}
157182
}
158183
}
159184
}
@@ -303,4 +328,8 @@ object OudsChip {
303328
internal enum class State {
304329
Enabled, Hovered, Pressed, Disabled, Focused
305330
}
331+
332+
internal enum class IconPosition {
333+
Start, End
334+
}
306335
}

core/src/main/java/com/orange/ouds/core/component/OudsFilterChip.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ fun OudsFilterChip(
8787
}
8888

8989
@Composable
90-
@JvmName("OudsChipNullableLabelAndIcon")
90+
@JvmName("OudsFilterChipNullableLabelAndIcon")
9191
private fun OudsFilterChip(
9292
selected: Boolean,
9393
onClick: () -> Unit,
@@ -98,10 +98,12 @@ private fun OudsFilterChip(
9898
interactionSource: MutableInteractionSource? = null
9999
) {
100100
OudsChip(
101+
selectable = true,
101102
selected = selected,
102103
onClick = onClick,
103-
nullableLabel = nullableLabel,
104-
nullableIcon = nullableIcon,
104+
label = nullableLabel,
105+
icon = nullableIcon,
106+
iconPosition = OudsChip.IconPosition.End,
105107
modifier = modifier,
106108
enabled = enabled,
107109
interactionSource = interactionSource
@@ -141,5 +143,5 @@ private val previewParameterValues: List<OudsFilterChipPreviewParameter>
141143
OudsFilterChipPreviewParameter(selected = true, hasLabel = true, hasIcon = false),
142144
OudsFilterChipPreviewParameter(selected = false, hasLabel = true, hasIcon = false),
143145
OudsFilterChipPreviewParameter(selected = true, hasLabel = false, hasIcon = true),
144-
OudsFilterChipPreviewParameter(selected = false, hasLabel = false, hasIcon = true),
146+
OudsFilterChipPreviewParameter(selected = false, hasLabel = false, hasIcon = true)
145147
)
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Software Name: OUDS Android
3+
* SPDX-FileCopyrightText: Copyright (c) Orange SA
4+
* SPDX-License-Identifier: MIT
5+
*
6+
* This software is distributed under the MIT license,
7+
* the text of which is available at https://opensource.org/license/MIT/
8+
* or see the "LICENSE" file for more details.
9+
*
10+
* Software description: Android library of reusable graphical components
11+
*/
12+
13+
package com.orange.ouds.core.component
14+
15+
import androidx.compose.foundation.interaction.MutableInteractionSource
16+
import androidx.compose.foundation.isSystemInDarkTheme
17+
import androidx.compose.material.icons.Icons
18+
import androidx.compose.material.icons.filled.FavoriteBorder
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.ui.Modifier
21+
import androidx.compose.ui.tooling.preview.PreviewLightDark
22+
import androidx.compose.ui.tooling.preview.PreviewParameter
23+
import com.orange.ouds.core.utilities.OudsPreview
24+
import com.orange.ouds.core.utilities.PreviewStates
25+
import com.orange.ouds.foundation.utilities.BasicPreviewParameterProvider
26+
27+
@Composable
28+
fun OudsSuggestionChip(
29+
onClick: () -> Unit,
30+
label: String,
31+
modifier: Modifier = Modifier,
32+
enabled: Boolean = true,
33+
interactionSource: MutableInteractionSource? = null
34+
) {
35+
OudsSuggestionChip(
36+
onClick = onClick,
37+
nullableLabel = label,
38+
nullableIcon = null,
39+
modifier = modifier,
40+
enabled = enabled,
41+
interactionSource = interactionSource
42+
)
43+
}
44+
45+
@Composable
46+
fun OudsSuggestionChip(
47+
onClick: () -> Unit,
48+
icon: OudsChip.Icon,
49+
modifier: Modifier = Modifier,
50+
enabled: Boolean = true,
51+
interactionSource: MutableInteractionSource? = null
52+
) {
53+
OudsSuggestionChip(
54+
onClick = onClick,
55+
nullableLabel = null,
56+
nullableIcon = icon,
57+
modifier = modifier,
58+
enabled = enabled,
59+
interactionSource = interactionSource
60+
)
61+
}
62+
63+
@Composable
64+
fun OudsSuggestionChip(
65+
onClick: () -> Unit,
66+
label: String,
67+
icon: OudsChip.Icon,
68+
modifier: Modifier = Modifier,
69+
enabled: Boolean = true,
70+
interactionSource: MutableInteractionSource? = null
71+
) {
72+
OudsSuggestionChip(
73+
onClick = onClick,
74+
nullableLabel = label,
75+
nullableIcon = icon,
76+
modifier = modifier,
77+
enabled = enabled,
78+
interactionSource = interactionSource
79+
)
80+
}
81+
82+
@Composable
83+
@JvmName("OudsSuggestionChipNullableLabelAndIcon")
84+
private fun OudsSuggestionChip(
85+
onClick: () -> Unit,
86+
nullableLabel: String?,
87+
nullableIcon: OudsChip.Icon?,
88+
modifier: Modifier = Modifier,
89+
enabled: Boolean = true,
90+
interactionSource: MutableInteractionSource? = null
91+
) {
92+
OudsChip(
93+
selectable = false,
94+
selected = false,
95+
onClick = onClick,
96+
label = nullableLabel,
97+
icon = nullableIcon,
98+
iconPosition = OudsChip.IconPosition.Start,
99+
modifier = modifier,
100+
enabled = enabled,
101+
interactionSource = interactionSource
102+
)
103+
}
104+
105+
@PreviewLightDark
106+
@Composable
107+
@Suppress("PreviewShouldNotBeCalledRecursively")
108+
private fun PreviewOudsSuggestionChip(@PreviewParameter(OudsSuggestionChipPreviewParameterProvider::class) parameter: OudsSuggestionChipPreviewParameter) {
109+
PreviewOudsSuggestionChip(darkThemeEnabled = isSystemInDarkTheme(), parameter = parameter)
110+
}
111+
112+
@Composable
113+
internal fun PreviewOudsSuggestionChip(darkThemeEnabled: Boolean, parameter: OudsSuggestionChipPreviewParameter) =
114+
OudsPreview(darkThemeEnabled = darkThemeEnabled) {
115+
with(parameter) {
116+
val label = if (hasLabel) "Label" else null
117+
val icon = if (hasIcon) OudsChip.Icon(Icons.Filled.FavoriteBorder, "") else null
118+
PreviewStates<OudsChip.State>(columnCount = 3) {
119+
OudsSuggestionChip(nullableIcon = icon, nullableLabel = label, onClick = {})
120+
}
121+
}
122+
}
123+
124+
internal data class OudsSuggestionChipPreviewParameter(
125+
val hasLabel: Boolean,
126+
val hasIcon: Boolean
127+
)
128+
129+
internal class OudsSuggestionChipPreviewParameterProvider :
130+
BasicPreviewParameterProvider<OudsSuggestionChipPreviewParameter>(*previewParameterValues.toTypedArray())
131+
132+
private val previewParameterValues: List<OudsSuggestionChipPreviewParameter>
133+
get() = listOf(
134+
OudsSuggestionChipPreviewParameter(hasLabel = true, hasIcon = true),
135+
OudsSuggestionChipPreviewParameter(hasLabel = true, hasIcon = false),
136+
OudsSuggestionChipPreviewParameter(hasLabel = false, hasIcon = true)
137+
)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Software Name: OUDS Android
3+
* SPDX-FileCopyrightText: Copyright (c) Orange SA
4+
* SPDX-License-Identifier: MIT
5+
*
6+
* This software is distributed under the MIT license,
7+
* the text of which is available at https://opensource.org/license/MIT/
8+
* or see the "LICENSE" file for more details.
9+
*
10+
* Software description: Android library of reusable graphical components
11+
*/
12+
13+
package com.orange.ouds.core.component.samples
14+
15+
import androidx.compose.material.icons.Icons
16+
import androidx.compose.material.icons.filled.FavoriteBorder
17+
import androidx.compose.runtime.Composable
18+
import com.orange.ouds.core.component.OudsChip
19+
import com.orange.ouds.core.component.OudsSuggestionChip
20+
21+
22+
@Composable
23+
internal fun OudsSuggestionChipTextOnlySample() {
24+
OudsSuggestionChip(
25+
onClick = { /* Do something! */ },
26+
label = "Suggestion chip"
27+
)
28+
}
29+
30+
@Composable
31+
internal fun OudsSuggestionChipIconOnlySample() {
32+
OudsSuggestionChip(
33+
onClick = { /* Do something! */ },
34+
icon = OudsChip.Icon(
35+
Icons.Filled.FavoriteBorder,
36+
"Content description"
37+
)
38+
)
39+
}
40+
41+
@Composable
42+
internal fun OudsSuggestionTextAndIconSample() {
43+
OudsSuggestionChip(
44+
onClick = { /* Do something! */ },
45+
label = "Label",
46+
icon = OudsChip.Icon(
47+
Icons.Filled.FavoriteBorder,
48+
"Content description"
49+
)
50+
)
51+
}

0 commit comments

Comments
 (0)