From 5bec64f9fb8d42b5fb6dd7e53329e0fe49708f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Scott=20Rayapoull=C3=A9?= Date: Tue, 24 Oct 2023 14:16:48 +0200 Subject: [PATCH] chore(tag): Add missing test, examples, configurator for tag component (#785) --- .../samples/buttons/ButtonsConfigurator.kt | 5 +- .../samples/tags/TagsConfigurator.kt | 218 ++++++++++++++++++ .../examples/samples/tags/TagsExamples.kt | 141 +++++++++++ .../spark/catalog/model/Components.kt | 16 ++ .../drawable-hdpi/illu_component_tags.webp | Bin 0 -> 3930 bytes .../drawable-mdpi/illu_component_tags.webp | Bin 0 -> 2634 bytes .../drawable-xhdpi/illu_component_tags.webp | Bin 0 -> 4976 bytes .../drawable-xxhdpi/illu_component_tags.webp | Bin 0 -> 7232 bytes .../drawable-xxxhdpi/illu_component_tags.webp | Bin 0 -> 9154 bytes catalog/src/main/res/values/strings.xml | 2 + .../com/adevinta/spark/tags/TagsScreenshot.kt | 163 +++++++++++++ ...rk.tags_TagsScreenshot_themesTags_dark.png | 3 + ...k.tags_TagsScreenshot_themesTags_light.png | 3 + ...eview_tests_tags_tagfilledpreview_dark.png | 4 +- ...view_tests_tags_tagfilledpreview_light.png | 4 +- ...iew_tests_tags_tagoutlinedpreview_dark.png | 4 +- ...ew_tests_tags_tagoutlinedpreview_light.png | 4 +- ...review_tests_tags_tagtonalpreview_dark.png | 4 +- ...eview_tests_tags_tagtonalpreview_light.png | 4 +- .../com/adevinta/spark/components/tags/Tag.kt | 44 +++- .../com/adevinta/spark/components/tags/Tag.md | 17 +- .../spark/components/tags/TagFilled.kt | 9 +- .../spark/components/tags/TagOutlined.kt | 9 +- .../spark/components/tags/TagTinted.kt | 12 +- .../spark/res/AnnotatedStringResource.kt | 4 +- 25 files changed, 624 insertions(+), 46 deletions(-) create mode 100644 catalog/src/main/kotlin/com/adevinta/spark/catalog/configurator/samples/tags/TagsConfigurator.kt create mode 100644 catalog/src/main/kotlin/com/adevinta/spark/catalog/examples/samples/tags/TagsExamples.kt create mode 100644 catalog/src/main/res/drawable-hdpi/illu_component_tags.webp create mode 100644 catalog/src/main/res/drawable-mdpi/illu_component_tags.webp create mode 100644 catalog/src/main/res/drawable-xhdpi/illu_component_tags.webp create mode 100644 catalog/src/main/res/drawable-xxhdpi/illu_component_tags.webp create mode 100644 catalog/src/main/res/drawable-xxxhdpi/illu_component_tags.webp create mode 100644 spark-screenshot-testing/src/test/kotlin/com/adevinta/spark/tags/TagsScreenshot.kt create mode 100644 spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark.tags_TagsScreenshot_themesTags_dark.png create mode 100644 spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark.tags_TagsScreenshot_themesTags_light.png diff --git a/catalog/src/main/kotlin/com/adevinta/spark/catalog/configurator/samples/buttons/ButtonsConfigurator.kt b/catalog/src/main/kotlin/com/adevinta/spark/catalog/configurator/samples/buttons/ButtonsConfigurator.kt index 620fb3652..53197a99e 100644 --- a/catalog/src/main/kotlin/com/adevinta/spark/catalog/configurator/samples/buttons/ButtonsConfigurator.kt +++ b/catalog/src/main/kotlin/com/adevinta/spark/catalog/configurator/samples/buttons/ButtonsConfigurator.kt @@ -39,7 +39,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -279,7 +278,7 @@ private fun ConfiguredButton( ) { val containerColor by animateColorAsState( targetValue = if (intent != ButtonIntent.Surface) { - Color.Transparent + SparkTheme.colors.backgroundVariant } else { SparkTheme.colors.surfaceInverse }, @@ -289,7 +288,7 @@ private fun ConfiguredButton( color = containerColor, ) { Box( - modifier = Modifier.padding(4.dp), + modifier = Modifier.padding(8.dp), ) { when (style) { ButtonStyle.Filled -> ButtonFilled( diff --git a/catalog/src/main/kotlin/com/adevinta/spark/catalog/configurator/samples/tags/TagsConfigurator.kt b/catalog/src/main/kotlin/com/adevinta/spark/catalog/configurator/samples/tags/TagsConfigurator.kt new file mode 100644 index 000000000..ee9af802e --- /dev/null +++ b/catalog/src/main/kotlin/com/adevinta/spark/catalog/configurator/samples/tags/TagsConfigurator.kt @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2023 Adevinta + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.adevinta.spark.catalog.configurator.samples.tags + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.adevinta.spark.SparkTheme +import com.adevinta.spark.catalog.model.Configurator +import com.adevinta.spark.catalog.themes.SegmentedButton +import com.adevinta.spark.catalog.util.SampleSourceUrl +import com.adevinta.spark.components.icons.FilledTonalIconToggleButton +import com.adevinta.spark.components.icons.Icon +import com.adevinta.spark.components.menu.DropdownMenuItem +import com.adevinta.spark.components.surface.Surface +import com.adevinta.spark.components.tags.TagFilled +import com.adevinta.spark.components.tags.TagIntent +import com.adevinta.spark.components.tags.TagOutlined +import com.adevinta.spark.components.tags.TagTinted +import com.adevinta.spark.components.text.Text +import com.adevinta.spark.components.textfields.SelectTextField +import com.adevinta.spark.components.textfields.TextField +import com.adevinta.spark.icons.LikeFill +import com.adevinta.spark.icons.SparkIcon +import com.adevinta.spark.icons.SparkIcons + +public val TagsConfigurator: Configurator = Configurator( + name = "Tag", + description = "Tag configuration", + sourceUrl = "$SampleSourceUrl/TagSamples.kt", +) { + TagSample() +} + +@Suppress("DEPRECATION") +@Preview( + showBackground = true, +) +@Composable +private fun TagSample() { + val scrollState = rememberScrollState() + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.verticalScroll(scrollState), + ) { + var icon: SparkIcon? by remember { mutableStateOf(null) } + var style by remember { mutableStateOf(TagStyle.Filled) } + var intent by remember { mutableStateOf(TagIntent.Main) } + var buttonText by remember { mutableStateOf("Filled Tag") } + + ConfigedTag( + modifier = Modifier.align(Alignment.CenterHorizontally), + style = style, + tagText = buttonText, + intent = intent, + icon = icon, + ) + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Absolute.spacedBy(8.dp), + ) { + Text( + text = "With Icon", + modifier = Modifier + .weight(1f) + .padding(bottom = 8.dp), + style = SparkTheme.typography.body2.copy(fontWeight = FontWeight.Bold), + ) + FilledTonalIconToggleButton( + checked = icon != null, + onCheckedChange = { + icon = if (it) SparkIcons.LikeFill else null + }, + ) { + Icon( + sparkIcon = SparkIcons.LikeFill, + contentDescription = null, + ) + } + } + + Column { + Text( + text = "Style", + modifier = Modifier.padding(bottom = 8.dp), + style = SparkTheme.typography.body2.copy(fontWeight = FontWeight.Bold), + ) + val tagStyles = TagStyle.entries + val tagStylesLabel = tagStyles.map { it.name } + SegmentedButton( + options = tagStylesLabel, + selectedOption = style.name, + onOptionSelect = { style = TagStyle.valueOf(it) }, + modifier = Modifier + .fillMaxWidth() + .height(48.dp), + ) + } + + val intents = TagIntent.entries + var expanded by remember { mutableStateOf(false) } + SelectTextField( + modifier = Modifier.fillMaxWidth(), + value = intent.name, + onValueChange = {}, + readOnly = true, + label = "Intent", + expanded = expanded, + onExpandedChange = { + expanded = !expanded + }, + onDismissRequest = { + expanded = false + }, + dropdownContent = { + intents.forEach { + DropdownMenuItem( + text = { Text(it.name) }, + onClick = { + intent = it + expanded = false + }, + ) + } + }, + ) + + TextField( + modifier = Modifier.fillMaxWidth(), + value = buttonText, + onValueChange = { + buttonText = it + }, + label = "Tag text", + placeholder = "Vérifier les Disponibilité", + ) + } +} + +@Composable +private fun ConfigedTag( + modifier: Modifier = Modifier, + style: TagStyle, + tagText: String, + intent: TagIntent, + icon: SparkIcon?, +) { + Surface( + modifier = modifier, + color = SparkTheme.colors.backgroundVariant, + ) { + Box( + modifier = Modifier.padding(8.dp), + ) { + when (style) { + TagStyle.Filled -> TagFilled( + text = tagText, + intent = intent, + leadingIcon = icon, + ) + + TagStyle.Outlined -> TagOutlined( + text = tagText, + intent = intent, + leadingIcon = icon, + ) + + TagStyle.Tinted -> TagTinted( + text = tagText, + intent = intent, + leadingIcon = icon, + ) + } + } + } +} + +private enum class TagStyle { + Filled, + Outlined, + Tinted, +} diff --git a/catalog/src/main/kotlin/com/adevinta/spark/catalog/examples/samples/tags/TagsExamples.kt b/catalog/src/main/kotlin/com/adevinta/spark/catalog/examples/samples/tags/TagsExamples.kt new file mode 100644 index 000000000..4e091b0ab --- /dev/null +++ b/catalog/src/main/kotlin/com/adevinta/spark/catalog/examples/samples/tags/TagsExamples.kt @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2023 Adevinta + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.adevinta.spark.catalog.examples.samples.tags + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.dp +import com.adevinta.spark.catalog.model.Example +import com.adevinta.spark.catalog.util.SampleSourceUrl +import com.adevinta.spark.components.tags.TagFilled +import com.adevinta.spark.components.tags.TagIntent +import com.adevinta.spark.components.tags.TagOutlined +import com.adevinta.spark.components.tags.TagTinted +import com.adevinta.spark.icons.Booster +import com.adevinta.spark.icons.SparkIcon +import com.adevinta.spark.icons.SparkIcons + +private const val TagsExampleDescription = "Tags examples" +private const val TagsExampleSourceUrl = "$SampleSourceUrl/TagSamples.kt" + +@OptIn(ExperimentalLayoutApi::class) +public val TagsExamples: List = listOf( + Example( + name = "Filled Tag", + description = TagsExampleDescription, + sourceUrl = TagsExampleSourceUrl, + ) { + TagSample( + tag = { text, intent, leadingIcon -> + TagFilled( + text = text, + intent = intent, + leadingIcon = leadingIcon, + ) + }, + ) + }, + Example( + name = "Tinted Tag", + description = TagsExampleDescription, + sourceUrl = TagsExampleSourceUrl, + ) { + TagSample( + tag = { text, intent, leadingIcon -> + TagTinted( + text = text, + intent = intent, + leadingIcon = leadingIcon, + ) + }, + ) + }, + Example( + name = "Outlined Tag", + description = TagsExampleDescription, + sourceUrl = TagsExampleSourceUrl, + ) { + TagSample( + tag = { text, intent, leadingIcon -> + TagOutlined( + text = text, + intent = intent, + leadingIcon = leadingIcon, + ) + }, + ) + }, + Example( + name = "Tag layouts", + description = "Showcase how to layout tags sao that they don't clip on parent width but can go to a new " + + "line if they don't fit", + sourceUrl = TagsExampleSourceUrl, + ) { + FlowRow( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + maxItemsInEachRow = 4, + ) { + TagFilled(text = "Tag 1", intent = TagIntent.Main) + TagFilled(text = "Tag longer 2", intent = TagIntent.Accent) + TagFilled(text = "Tag a bit longer 3", intent = TagIntent.Info) + TagTinted(text = "Tag way more longer 4", intent = TagIntent.Main) + TagTinted(text = "Tag small 5", intent = TagIntent.Main) + TagOutlined(text = "Tag 6", intent = TagIntent.Main) + } + }, +) + +@Composable +private fun TagSample( + tag: @Composable ( + text: String, + intent: TagIntent, + leadingIcon: SparkIcon?, + ) -> Unit, +) { + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + val icon = SparkIcons.Booster + val tagText = "available" + + tag( + /* text = */ tagText, + /* intent = */TagIntent.Main, + /* leadingIcon = */ null, + ) + tag( + /* text = */ tagText, + /* intent = */TagIntent.Main, + /* leadingIcon = */ icon, + ) + tag( + /* text = */ "", + /* intent = */TagIntent.Main, + /* leadingIcon = */ icon, + ) + } +} diff --git a/catalog/src/main/kotlin/com/adevinta/spark/catalog/model/Components.kt b/catalog/src/main/kotlin/com/adevinta/spark/catalog/model/Components.kt index 49350ff29..d38a6271e 100644 --- a/catalog/src/main/kotlin/com/adevinta/spark/catalog/model/Components.kt +++ b/catalog/src/main/kotlin/com/adevinta/spark/catalog/model/Components.kt @@ -28,6 +28,7 @@ import com.adevinta.spark.catalog.configurator.samples.buttons.ButtonsConfigurat import com.adevinta.spark.catalog.configurator.samples.buttons.IconButtonsConfigurator import com.adevinta.spark.catalog.configurator.samples.buttons.IconToggleButtonsConfigurator import com.adevinta.spark.catalog.configurator.samples.tabs.TabsConfigurator +import com.adevinta.spark.catalog.configurator.samples.tags.TagsConfigurator import com.adevinta.spark.catalog.configurator.samples.textfields.TextFieldsConfigurator import com.adevinta.spark.catalog.configurator.samples.toggles.CheckboxConfigurator import com.adevinta.spark.catalog.configurator.samples.toggles.RadioButtonConfigurator @@ -35,6 +36,7 @@ import com.adevinta.spark.catalog.configurator.samples.toggles.SwitchConfigurato import com.adevinta.spark.catalog.examples.samples.buttons.ButtonsExamples import com.adevinta.spark.catalog.examples.samples.buttons.IconButtonsExamples import com.adevinta.spark.catalog.examples.samples.tabs.TabsExamples +import com.adevinta.spark.catalog.examples.samples.tags.TagsExamples import com.adevinta.spark.catalog.examples.samples.toggles.CheckboxExamples import com.adevinta.spark.catalog.examples.samples.toggles.IconToggleButtonsExamples import com.adevinta.spark.catalog.examples.samples.toggles.RadioButtonExamples @@ -151,6 +153,19 @@ private val Tabs = Component( configurator = TabsConfigurator, ) +private val Tags = Component( + id = nextId(), + name = "Tags", + description = R.string.component_tag_description, + illustration = R.drawable.illu_component_tags, + tintIcon = false, + guidelinesUrl = "$ComponentGuidelinesUrl/p/295e88-tag/b/86ead2", + docsUrl = "$PackageSummaryUrl/com.adevinta.spark.components.tags/index.html", + sourceUrl = "$SparkSourceUrl/kotlin/com/adevinta/components/tags/Tag.kt", + examples = TagsExamples, + configurator = TagsConfigurator, +) + private val TextFields = Component( id = nextId(), name = "TextFields", @@ -185,5 +200,6 @@ public val Components: List = listOf( RadioButtons, Switches, Tabs, + Tags, TextFields, ) diff --git a/catalog/src/main/res/drawable-hdpi/illu_component_tags.webp b/catalog/src/main/res/drawable-hdpi/illu_component_tags.webp new file mode 100644 index 0000000000000000000000000000000000000000..5ef92e38d3a81891594cbe89c2f1552e99e6074a GIT binary patch literal 3930 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 literal 0 HcmV?d00001 diff --git a/catalog/src/main/res/drawable-mdpi/illu_component_tags.webp b/catalog/src/main/res/drawable-mdpi/illu_component_tags.webp new file mode 100644 index 0000000000000000000000000000000000000000..54ca4ded370fbb81ac6625c71b63b2a6411c5e31 GIT binary patch literal 2634 zcmV-Q3bpl8Nk&FO3IG6CMM6+kP&il$0000G0001*005%^06|PpNTCD(009|AZQDTd zyX8-K2Zo6LPk?6-=-4?5(l%f=|FXY?h)9SB96*u&|EGuvNMG2t8IrW^-*H7mWmWgo zZ9KMZ+qP|6OSWy>wr$%s$K}k7I2StA$Xa?35fdQ&@Be>PH**dHvkFN`b&gU!WzLyP zh1gaL+TvWDNK(?yF~hb<+Cjm`GO~4e>dB))I_?bPfnke3%>Mb0n5yE5?ss(TeP>Ft zW+_n{d>MCyg`xyI@pR|7?77c!$_Znmah#N@N@M?{0|TLO@J~yjh>L&u_x`^fIm8a$ z7P&E*u5Wk!VjI)ohgys^x0=d2?yrbjY6n9Yh^W( z!cBj0kJsT+S3dH{yPkAf3gwr9a?Gv7uoYYOWc*L_IGN7Nc#1p=Ij~_Zy7o){F zAHibi>20UwC4O+*cZR5T*^<|uuH2xvWoWb`w>o1vpe^#FcW%%%g4`q3?;hd)*w;Iyl7Q^9}-78Zl&-l8VMVFem$o;N^ zr_X)7*okktr{TEio!-1TZ(zQ-?(@mBUc;r^ed}MR%0>UEi_Ak=H^|TS>fvI8em5&G zEeyEOE#IBZYkbaQ&N$mg-O{*&>A?}rJ^oyA8zi|BjLlD&y+)q+s+k_SX*eOG3DlbqJaOs@V} zrsICoYRtt926IN$_*WmM?bJ0D$3*d^DBQ&$h#nv zTvZVvfBWI5fzecKqYEw;Kse>NQ%^Z{BtU~h$6oxDGoED(P-32K zEXy?c*L443XDK_)IHhowlj$=5!{7coIk3Me3h4 z1Xxav{OUJ{{FZK+y$(MYct@n8c>mj99 zO&`dw);t3}4#o%ww=fR@7UluqBHX|{1Y4K~fO{Q^MtmlyCyf?dGCxl~tG1pH^S}=t zvD%4GPe010pY+&!=gn8|IpTrpFa zQ?xF8kgk0bD-@v4z7{t>ijLy_gn^c>b!8AR2nBHbws+vN>D26Y#@k@PqgZtC*9{N8 zrmOtTLO2YUv|aZ!z;e~14IN|`g&(SBw?4-GYLhS=Xf%R7Ah7z70e4NU6=9(9{iHTe zB`$gb;|LzjaDWxL1*6j-R-u%2=r8a+%Jt4e$5S9CsR&MZi`;soP%P>vP3@HaWUt3Q za?9F*;lkk$?Us*CJe^5#X78=`gpJ! zOZgx43%lu&(Kd8eKK_SW+7m; zap+F=M}6J122rL_%*nRW>q&O_WFQ;$4xr_lD!da)V+ptlcRQmvSHwFm)<)_*fwV7$ z|7uL?Sw{aO^L;aXet3#DzVx@UJ;ai>nrMFY@%8&YN$7h;Cg- zI>F1ihNY^1=;QBba2{zJX-2I4_pSMgK8eh8WCexO5a1seoh$thvx+EN9FQIQN=NPd ztOPCLv}P7e-1AhepwqqL%#OR-tnwNAPN6VZIgfqdZE(JrT7q#Wiehg8@Hl?ZnKhh+e1t*9u zF33XrWE7s7^>*T9WN`Zsfo9-HX9h^yV;44sWT~f8Z-XVn`GzX07wEK&TghvV3W7*C#4^#K+!g zy!(KerxTmVJp>_P9`|N;EJy6X%%dz%GEcU~J}0<9{k;iS9NGFgaU#5Bg|r{pWHcRb zOgpAn=|0$=5h(xka#Wg@oQV1^?oemR6%Z#h5W`fW)9F!m`dl{^9|X{RnjonRZ07kQj0000003rw;6aWAK literal 0 HcmV?d00001 diff --git a/catalog/src/main/res/drawable-xhdpi/illu_component_tags.webp b/catalog/src/main/res/drawable-xhdpi/illu_component_tags.webp new file mode 100644 index 0000000000000000000000000000000000000000..ff5187b8429b1241666f205138a9844f3d1b594a GIT binary patch literal 4976 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 X`B270@*&H0#su9))&DIw|Bn9w(=n2} literal 0 HcmV?d00001 diff --git a/catalog/src/main/res/drawable-xxhdpi/illu_component_tags.webp b/catalog/src/main/res/drawable-xxhdpi/illu_component_tags.webp new file mode 100644 index 0000000000000000000000000000000000000000..22bc324c89563f814f03e9bc02886b970214cca4 GIT binary patch literal 7232 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�@>=#_ZZPxIPv3Q%JS*kCa@&U)(eUj;O zdUf%p*Wz81QLn`slG6Mu%})gu-Wpj2>a~SV5#Z6N*!oY1i4Ng<-c!Jb?Pko+SHBq z5ZaklD60WZaShn*&b*GLMJfS4m(k1L{io;4hgqy7>JgqKa@33vtN^OMD*XxGKS3n^ zx%H)(gfs7FQW&L0J$HByl%r8Rb317!_e^TZl5J4S{%o}q&G+nz@f?eIFRIese}0XY z@XX8Je zc#l(?GlCVRSZW<6tJMFFMt96)ON9e`FgLMO$u4d~S)|XE`-a8F#%W#WMeuIwnE*Oq zdA88hBxW>DD1THeabb3B!4IrWY;xcsO^;04<7>u-dg5^X#2f5&m%`Cvm2j)~jzDn4!YeRQC;5_Zcl&{3FE&d(uyMVpI44HQyW8$nSK& z6L`GDOMyC|8Mk6X1;H-cLn^dVTjkCyt7W)_D%mZKjm9mF=07-O)NER5x%Eivr`aC? za+{#rG^hZF~&(|1s1bG=L43l@AV&n!-_2!CKds8UO$Q literal 0 HcmV?d00001 diff --git a/catalog/src/main/res/drawable-xxxhdpi/illu_component_tags.webp b/catalog/src/main/res/drawable-xxxhdpi/illu_component_tags.webp new file mode 100644 index 0000000000000000000000000000000000000000..ab30d22e7f289fbe8b37b0865534a0cba62e949e GIT binary patch literal 9154 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 literal 0 HcmV?d00001 diff --git a/catalog/src/main/res/values/strings.xml b/catalog/src/main/res/values/strings.xml index ba61ad5b0..169102d33 100644 --- a/catalog/src/main/res/values/strings.xml +++ b/catalog/src/main/res/values/strings.xml @@ -64,6 +64,8 @@ Component used when only one choice may be selected in a series of options. Switch component allows the user to activate or deactivate the state of an element or concept. It is also used to control binary options (On/Off or True/False). + + Tags are used to label content and help users quickly recognize info about them: Categories, Status… Can be applied with different colors and designs that are associated with a content due to its characteristics: new content, unvisited content, featured content… Users can’t interact with Tags. Components configurations The Configurator allows to visualize a component in every possible state available by customizing every property for each component. diff --git a/spark-screenshot-testing/src/test/kotlin/com/adevinta/spark/tags/TagsScreenshot.kt b/spark-screenshot-testing/src/test/kotlin/com/adevinta/spark/tags/TagsScreenshot.kt new file mode 100644 index 000000000..7376d67de --- /dev/null +++ b/spark-screenshot-testing/src/test/kotlin/com/adevinta/spark/tags/TagsScreenshot.kt @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2023 Adevinta + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.adevinta.spark.tags + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import app.cash.paparazzi.DeviceConfig +import app.cash.paparazzi.Paparazzi +import com.adevinta.spark.MaxPercentDifference +import com.adevinta.spark.PaparazziTheme +import com.adevinta.spark.SparkTheme +import com.adevinta.spark.components.surface.Surface +import com.adevinta.spark.components.tags.TagFilled +import com.adevinta.spark.components.tags.TagIntent +import com.adevinta.spark.components.tags.TagOutlined +import com.adevinta.spark.components.tags.TagTinted +import com.adevinta.spark.components.text.Text +import com.adevinta.spark.icons.FireFill +import com.adevinta.spark.icons.SparkIcon +import com.adevinta.spark.icons.SparkIcons +import com.adevinta.spark.sparkSnapshot +import com.adevinta.spark.tags.TagsScreenshot.Style.Filled +import com.adevinta.spark.tags.TagsScreenshot.Style.Outlined +import com.adevinta.spark.tags.TagsScreenshot.Style.Tinted +import com.adevinta.spark.tokens.darkSparkColors +import com.adevinta.spark.tokens.lightSparkColors +import com.adevinta.spark.tools.preview.ThemeVariant +import com.adevinta.spark.tools.preview.ThemeVariant.Light +import com.android.ide.common.rendering.api.SessionParams +import org.junit.Rule +import org.junit.Test + +internal class TagsScreenshot { + + private val values: List = listOf(stubBody, stubShortBody, "") + private val icon: List = listOf(SparkIcons.FireFill, null) + + enum class Style { + Tinted, + Filled, + Outlined, + } + + @get:Rule + val paparazzi = Paparazzi( + maxPercentDifference = MaxPercentDifference, + theme = PaparazziTheme, + renderingMode = SessionParams.RenderingMode.V_SCROLL, + deviceConfig = DeviceConfig.PIXEL_C.copy( + softButtons = false, + locale = "fr-rFR", + ), + showSystemUi = true, + ) + + @Test + fun themesTags() { + ThemeVariant.entries.forEach { + paparazzi.sparkSnapshot(name = it.name) { + SparkTheme( + colors = if (it == Light) lightSparkColors() else darkSparkColors(), + ) { + Surface( + color = SparkTheme.colors.backgroundVariant, + modifier = Modifier.fillMaxWidth(), + ) { + Tags() + } + } + } + } + } + + @OptIn(ExperimentalLayoutApi::class) + @Composable + private fun Tags() { + FlowRow( + horizontalArrangement = Arrangement.SpaceAround, + verticalArrangement = Arrangement.Center, + ) { + TagIntent.entries.forEach { intent -> + Row { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + text = intent.name, + style = SparkTheme.typography.headline1, + ) + Style.entries.forEach { style -> + Column( + modifier = Modifier.padding(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + values.forEach { value -> + for (icon in icon) { + // Invalid state to be skipped + if (value.isBlank() && icon == null) continue + Row { + when (style) { + Filled -> TagFilled( + text = value, + intent = intent, + leadingIcon = icon, + ) + + Outlined -> TagTinted( + text = value, + intent = intent, + leadingIcon = icon, + ) + + Tinted -> TagOutlined( + text = value, + intent = intent, + leadingIcon = icon, + ) + } + } + } + } + } + } + } + } + } + } + } + + companion object { + private const val stubShortBody = "Lorem" + private const val stubBody = "Lorem ipsum dolor sit" + } +} diff --git a/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark.tags_TagsScreenshot_themesTags_dark.png b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark.tags_TagsScreenshot_themesTags_dark.png new file mode 100644 index 000000000..69cd69a0f --- /dev/null +++ b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark.tags_TagsScreenshot_themesTags_dark.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b17ada9d51b7432fc5d99e4f36734b6d23a5e13196810791e339b371afab6b67 +size 209767 diff --git a/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark.tags_TagsScreenshot_themesTags_light.png b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark.tags_TagsScreenshot_themesTags_light.png new file mode 100644 index 000000000..d7d6a9a4e --- /dev/null +++ b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark.tags_TagsScreenshot_themesTags_light.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1664e493c736d5ccda50d9db4d91c5995686e41d35f4e82923b20702c9ee6400 +size 200899 diff --git a/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagfilledpreview_dark.png b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagfilledpreview_dark.png index 36b290ade..370127c7b 100644 --- a/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagfilledpreview_dark.png +++ b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagfilledpreview_dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c3515667b1df4c8677260de6ce825368bc891e282af5a791f070499ae9e0d0d -size 52662 +oid sha256:b96be10df0aaad6d30c453d447a2e30c0a4bff86100ec27cbf42509b83998442 +size 9772 diff --git a/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagfilledpreview_light.png b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagfilledpreview_light.png index 6bf15ff4c..0f7cf5153 100644 --- a/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagfilledpreview_light.png +++ b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagfilledpreview_light.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f92081554335145c9e667988865229fea6763e917f92010b1beb41677d75e6f -size 53272 +oid sha256:e53f0c6ff44e23773bdc75877d5628668d5cb4aaac930ae6be0dc19cf5fc7712 +size 9792 diff --git a/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagoutlinedpreview_dark.png b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagoutlinedpreview_dark.png index 6c019da16..8fbb714fc 100644 --- a/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagoutlinedpreview_dark.png +++ b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagoutlinedpreview_dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6475e05204b62e443b2cd8ff004e6c8106c80c660ad5f8b41df5e02c528370dc -size 59980 +oid sha256:736fde31d4d88ccd7e70b1aa371013faa45688bc70a43fdcf52587fcb889c4c7 +size 10972 diff --git a/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagoutlinedpreview_light.png b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagoutlinedpreview_light.png index 745daec4d..0e2623172 100644 --- a/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagoutlinedpreview_light.png +++ b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagoutlinedpreview_light.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:727680949322137f6468d561903f6d9e9e1bf38604b73b25cdaf12518b2d2273 -size 58487 +oid sha256:55c4daebca5c876e8d59006a36c0c3c5f710232a4550aaa6dff0aacd732d6905 +size 10805 diff --git a/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagtonalpreview_dark.png b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagtonalpreview_dark.png index 216b67eff..a28c7092f 100644 --- a/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagtonalpreview_dark.png +++ b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagtonalpreview_dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aad5cb76c246f8c2f91602b31156f3a1c56c0974cf375ea39512a51b90e7ffcf -size 54298 +oid sha256:a2d38242952417455999cab259aa931012c3df64f6c57214ee3c9288a8c773b9 +size 9947 diff --git a/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagtonalpreview_light.png b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagtonalpreview_light.png index 63b24fb11..fbce0b913 100644 --- a/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagtonalpreview_light.png +++ b/spark-screenshot-testing/src/test/snapshots/images/com.adevinta.spark_PreviewScreenshotTests_preview_tests_tags_tagtonalpreview_light.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cccd8d700d21e35843374a19ebd7986f88d06718015a37fe7a17ea4ffc4e81ba -size 52244 +oid sha256:345d8255189d9be5b4713ca3841e78ee5c7903dba157becbe7430ed2bd01d39d +size 9695 diff --git a/spark/src/main/kotlin/com/adevinta/spark/components/tags/Tag.kt b/spark/src/main/kotlin/com/adevinta/spark/components/tags/Tag.kt index 631199428..f523aab14 100644 --- a/spark/src/main/kotlin/com/adevinta/spark/components/tags/Tag.kt +++ b/spark/src/main/kotlin/com/adevinta/spark/components/tags/Tag.kt @@ -21,10 +21,12 @@ */ package com.adevinta.spark.components.tags +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -87,18 +89,24 @@ internal fun BaseSparkTag( horizontalArrangement = Arrangement.spacedBy(LeadingIconEndSpacing, Alignment.Start), verticalAlignment = Alignment.CenterVertically, ) { - if (leadingIcon != null) { - CompositionLocalProvider( - LocalContentColor provides colors.contentColor, - ) { - Icon( - sparkIcon = leadingIcon, - modifier = Modifier.size(LeadingIconSize), - contentDescription = null, // The tag is associated with a mandatory label so it's okay - tint = tint ?: LocalContentColor.current, - ) + AnimatedVisibility(visible = leadingIcon != null) { + if (leadingIcon != null) { + CompositionLocalProvider( + LocalContentColor provides colors.contentColor, + ) { + Icon( + sparkIcon = leadingIcon, + modifier = Modifier.size(LeadingIconSize), + contentDescription = null, // The tag is associated with a mandatory label so it's okay + tint = tint ?: LocalContentColor.current, + ) + } + } else { + // Placeholder so that the animation doesn't breaks when the icon disappears + Spacer(Modifier.size(LeadingIconSize)) } } + ProvideTextStyle(value = SparkTheme.typography.caption.copy(fontWeight = FontWeight.Bold)) { content() } @@ -117,6 +125,9 @@ internal fun SparkTag( leadingIcon: SparkIcon? = null, tint: Color? = null, ) { + require(text.isNotBlank() || leadingIcon != null) { + "text can be blank only when there is an icon" + } BaseSparkTag( colors = colors, modifier = modifier, @@ -124,7 +135,9 @@ internal fun SparkTag( leadingIcon = leadingIcon, tint = tint, ) { - Text(text = text) + if (text.isNotBlank()) { + Text(text = text) + } } } @@ -138,6 +151,9 @@ internal fun SparkTag( leadingIcon: SparkIcon? = null, tint: Color? = null, ) { + require(text.isNotBlank() || leadingIcon != null) { + "text can be blank only when there is an icon" + } BaseSparkTag( colors = colors, modifier = modifier, @@ -145,7 +161,9 @@ internal fun SparkTag( leadingIcon = leadingIcon, tint = tint, ) { - Text(text = text) + if (text.isNotBlank()) { + Text(text = text) + } } } @@ -231,5 +249,7 @@ private fun SparkTagPreview() { Text("À la une") } } + BaseSparkTag(leadingIcon = SparkIcons.Accessories, colors = colors) { + } } } diff --git a/spark/src/main/kotlin/com/adevinta/spark/components/tags/Tag.md b/spark/src/main/kotlin/com/adevinta/spark/components/tags/Tag.md index 496daaee5..36e66181d 100644 --- a/spark/src/main/kotlin/com/adevinta/spark/components/tags/Tag.md +++ b/spark/src/main/kotlin/com/adevinta/spark/components/tags/Tag.md @@ -20,11 +20,16 @@ The tags can also have a decorative start icon to better identify the context of We don't provide any container for now but we recommend using a [`FlowRow`](https://developer.android.com/jetpack/compose/layouts/flow) to layout it in your screens. ```kotlin -FlowRow(modifier = Modifier.padding(8.dp)) { - TagFilled("Price: High to Low") - TagFilled("Avg rating: 4+") - TagFilled("Free breakfast") - TagFilled("Free cancellation") - TagFilled("£50 pn") +FlowRow( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + maxItemsInEachRow = 4, +) { + TagFilled(text = "Tag 1", intent = TagIntent.Main) + TagFilled(text = "Tag longer 2", intent = TagIntent.Accent) + TagFilled(text = "Tag a bit longer 3", intent = TagIntent.Info) + TagTinted(text = "Tag way more longer 4", intent = TagIntent.Main) + TagTinted(text = "Tag small 5", intent = TagIntent.Main) + TagOutlined(text = "Tag 6", intent = TagIntent.Main) } ```` diff --git a/spark/src/main/kotlin/com/adevinta/spark/components/tags/TagFilled.kt b/spark/src/main/kotlin/com/adevinta/spark/components/tags/TagFilled.kt index 00beb34b1..672af9eb9 100644 --- a/spark/src/main/kotlin/com/adevinta/spark/components/tags/TagFilled.kt +++ b/spark/src/main/kotlin/com/adevinta/spark/components/tags/TagFilled.kt @@ -31,7 +31,9 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import com.adevinta.spark.PreviewTheme +import com.adevinta.spark.icons.Booster import com.adevinta.spark.icons.SparkIcon +import com.adevinta.spark.icons.SparkIcons import com.adevinta.spark.tools.preview.ThemeProvider import com.adevinta.spark.tools.preview.ThemeVariant @@ -115,8 +117,9 @@ internal fun TagFilledPreview( @PreviewParameter(ThemeProvider::class) theme: ThemeVariant, ) { PreviewTheme(theme) { - TagIntent.values().forEach { - TagFilled("Tag ${it.name}", intent = it) - } + val icon = SparkIcons.Booster + TagFilled("", leadingIcon = icon) + TagFilled("Tag Basic") + TagFilled("Tag Basic", leadingIcon = icon) } } diff --git a/spark/src/main/kotlin/com/adevinta/spark/components/tags/TagOutlined.kt b/spark/src/main/kotlin/com/adevinta/spark/components/tags/TagOutlined.kt index 09ddfcc25..ea640a889 100644 --- a/spark/src/main/kotlin/com/adevinta/spark/components/tags/TagOutlined.kt +++ b/spark/src/main/kotlin/com/adevinta/spark/components/tags/TagOutlined.kt @@ -32,7 +32,9 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import com.adevinta.spark.PreviewTheme +import com.adevinta.spark.icons.Booster import com.adevinta.spark.icons.SparkIcon +import com.adevinta.spark.icons.SparkIcons import com.adevinta.spark.tools.preview.ThemeProvider import com.adevinta.spark.tools.preview.ThemeVariant @@ -178,8 +180,9 @@ internal fun TagOutlinedPreview( @PreviewParameter(ThemeProvider::class) theme: ThemeVariant, ) { PreviewTheme(theme) { - TagIntent.values().forEach { - TagOutlined("Tag ${it.name}", intent = it) - } + val icon = SparkIcons.Booster + TagOutlined("", leadingIcon = icon) + TagOutlined("Tag Basic") + TagOutlined("Tag Basic", leadingIcon = icon) } } diff --git a/spark/src/main/kotlin/com/adevinta/spark/components/tags/TagTinted.kt b/spark/src/main/kotlin/com/adevinta/spark/components/tags/TagTinted.kt index e0583500e..63194d048 100644 --- a/spark/src/main/kotlin/com/adevinta/spark/components/tags/TagTinted.kt +++ b/spark/src/main/kotlin/com/adevinta/spark/components/tags/TagTinted.kt @@ -31,7 +31,9 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import com.adevinta.spark.PreviewTheme +import com.adevinta.spark.icons.Booster import com.adevinta.spark.icons.SparkIcon +import com.adevinta.spark.icons.SparkIcons import com.adevinta.spark.tools.preview.ThemeProvider import com.adevinta.spark.tools.preview.ThemeVariant @@ -154,11 +156,9 @@ internal fun TagTonalPreview( @PreviewParameter(ThemeProvider::class) theme: ThemeVariant, ) { PreviewTheme(theme) { - TagIntent.values().forEach { intent -> - TagTinted( - text = "Tag ${intent.name}", - intent = intent, - ) - } + val icon = SparkIcons.Booster + TagTinted("", leadingIcon = icon) + TagTinted("Tag Basic") + TagTinted("Tag Basic", leadingIcon = icon) } } diff --git a/spark/src/main/kotlin/com/adevinta/spark/res/AnnotatedStringResource.kt b/spark/src/main/kotlin/com/adevinta/spark/res/AnnotatedStringResource.kt index 27c5f7b28..034593f05 100644 --- a/spark/src/main/kotlin/com/adevinta/spark/res/AnnotatedStringResource.kt +++ b/spark/src/main/kotlin/com/adevinta/spark/res/AnnotatedStringResource.kt @@ -272,7 +272,9 @@ private fun TypefaceSpan.toSpanStyle() = SpanStyle( @Composable private fun AnnotatedStringResourcePreview() { PreviewTheme { - Text(annotatedStringResource(R.string.spark_annotatedStringResource_test)) + Text( + text = annotatedStringResource(R.string.spark_annotatedStringResource_test), + ) Text(annotatedStringResource(R.string.spark_annotatedStringResource_test_args, "Spark")) } }