Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-07 19:13:32 +03:00
parent a8744eb429
commit aaacc3d736
32 changed files with 1306 additions and 86 deletions

View file

@ -2,11 +2,19 @@ package com.tangem.feature.tester.presentation.storybook.entity
import com.tangem.core.ui.ds.badge.TangemBadgeColor
import com.tangem.core.ui.ds.field.search.TangemFieldShape
import com.tangem.core.ui.ds2.loader.TangemLoaderSize
import com.tangem.core.ui.ds.message.TangemMessageEffect
import com.tangem.core.ui.ds.topbar.TangemTopBarType
internal sealed interface StoryBookPage
/**
* Marker for pages that live inside the DS components sub-list.
* The view model uses it to route back navigation to the DS list
* instead of the root [StoryList].
*/
internal sealed interface DsStoryBookPage : StoryBookPage
internal data object StoryList : StoryBookPage
internal data object ButtonsStory : StoryBookPage
@ -83,4 +91,13 @@ internal data object PlaceholderStory : StoryBookPage
internal data object ProgressIndicatorStory : StoryBookPage
internal data object DeviceIconStory : StoryBookPage
internal data object DeviceIconStory : StoryBookPage
internal data class DsComponentsListStory(
val onStoryClick: (StoryPageFactory) -> Unit,
) : StoryBookPage
internal data class TangemLoaderStory(
val selectedSize: TangemLoaderSize,
val onSizeChange: (TangemLoaderSize) -> Unit,
) : DsStoryBookPage

View file

@ -0,0 +1,12 @@
package com.tangem.feature.tester.presentation.storybook.page.ds
import com.tangem.feature.tester.presentation.storybook.entity.DsComponentsListStory
import com.tangem.feature.tester.presentation.storybook.entity.StoryPageFactory
internal val dsComponentsListStoryFactory: StoryPageFactory = StoryPageFactory { updatePage ->
DsComponentsListStory(
onStoryClick = { factory ->
updatePage { factory.create(updatePage) }
},
)
}

View file

@ -0,0 +1,46 @@
package com.tangem.feature.tester.presentation.storybook.page.ds
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.tester.presentation.storybook.entity.DsComponentsListStory
import com.tangem.feature.tester.presentation.storybook.entity.StoryPageFactory
import com.tangem.feature.tester.presentation.storybook.page.ds.loader.tangemLoaderStoryFactory
private data class DsStoryItem(val title: String, val factory: StoryPageFactory)
private fun buildDsStories() = listOf(
DsStoryItem(title = "⏳ TangemLoader", factory = tangemLoaderStoryFactory),
)
@Composable
internal fun DsComponentsListStory(state: DsComponentsListStory, modifier: Modifier = Modifier) {
val stories = remember { buildDsStories() }
LazyColumn(
modifier = modifier
.statusBarsPadding()
.fillMaxSize()
.background(TangemTheme.colors.background.primary),
) {
items(items = stories, key = { it.title }) { item ->
PrimaryButton(
text = item.title,
onClick = { state.onStoryClick(item.factory) },
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
.fillMaxWidth(),
)
}
}
}

View file

@ -0,0 +1,18 @@
package com.tangem.feature.tester.presentation.storybook.page.ds.loader
import com.tangem.core.ui.ds2.loader.TangemLoaderSize
import com.tangem.feature.tester.presentation.storybook.entity.TangemLoaderStory
import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater
import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory
internal fun StateUpdater<TangemLoaderStory>.build(): TangemLoaderStory {
return TangemLoaderStory(
selectedSize = TangemLoaderSize.X24,
onSizeChange = { size ->
updateStory { it.copy(selectedSize = size) }
},
)
}
internal val tangemLoaderStoryFactory
get() = storyPageFactory(StateUpdater<TangemLoaderStory>::build)

View file

@ -0,0 +1,119 @@
@file:Suppress("MagicNumber")
package com.tangem.feature.tester.presentation.storybook.page.ds.loader
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.ds2.loader.TangemLoader
import com.tangem.core.ui.ds2.loader.TangemLoaderSize
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.tester.presentation.storybook.entity.TangemLoaderStory
@Composable
internal fun TangemLoaderStory(state: TangemLoaderStory, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.statusBarsPadding()
.fillMaxSize()
.background(TangemTheme.colors2.surface.level1),
verticalArrangement = Arrangement.spacedBy(24.dp),
) {
ComponentPreview(size = state.selectedSize)
SizeSelector(
selected = state.selectedSize,
onSelect = state.onSizeChange,
)
}
}
@Composable
private fun ComponentPreview(size: TangemLoaderSize) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxWidth()
.height(160.dp)
.padding(horizontal = 16.dp)
.clip(RoundedCornerShape(16.dp))
.background(TangemTheme.colors2.surface.level2),
) {
TangemLoader(size = size)
}
}
@Composable
private fun SizeSelector(selected: TangemLoaderSize, onSelect: (TangemLoaderSize) -> Unit) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
) {
Text(
text = "Size",
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
)
val shape = RoundedCornerShape(50)
Row(
modifier = Modifier
.fillMaxWidth()
.clip(shape)
.background(TangemTheme.colors2.surface.level2)
.border(
width = 1.dp,
color = TangemTheme.colors2.border.neutral.secondary,
shape = shape,
)
.padding(4.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
TangemLoaderSize.entries.forEach { size ->
SizeChip(
label = size.name,
selected = size == selected,
onClick = { onSelect(size) },
modifier = Modifier.weight(1f),
)
}
}
}
}
@Composable
private fun SizeChip(label: String, selected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
val chipShape = RoundedCornerShape(50)
Box(
contentAlignment = Alignment.Center,
modifier = modifier
.clip(chipShape)
.background(
if (selected) TangemTheme.colors2.surface.level3 else TangemTheme.colors2.surface.level2,
)
.clickable(onClick = onClick)
.padding(vertical = 8.dp, horizontal = 4.dp),
) {
Text(
text = label,
style = TangemTheme.typography.caption2,
color = if (selected) TangemTheme.colors.text.primary1 else TangemTheme.colors.text.secondary,
)
}
}

View file

@ -21,6 +21,7 @@ import com.tangem.feature.tester.presentation.storybook.page.buttons.buttonsStor
import com.tangem.feature.tester.presentation.storybook.page.checkbox.tangemCheckboxStoryFactory
import com.tangem.feature.tester.presentation.storybook.page.contextmenu.tangemContextMenuStoryFactory
import com.tangem.feature.tester.presentation.storybook.page.deviceicon.deviceIconStoryFactory
import com.tangem.feature.tester.presentation.storybook.page.ds.dsComponentsListStoryFactory
import com.tangem.feature.tester.presentation.storybook.page.headerrow.tangemHeaderRowStoryFactory
import com.tangem.feature.tester.presentation.storybook.page.message.tangemMessageStoryFactory
import com.tangem.feature.tester.presentation.storybook.page.opportunities.opportunitiesBGStoryFactory
@ -37,6 +38,7 @@ import com.tangem.feature.tester.presentation.storybook.page.typography.typograp
private data class StoryItem(val title: String, val factory: StoryPageFactory)
private fun buildStories() = listOf(
StoryItem(title = "💎 DS Components", factory = dsComponentsListStoryFactory),
StoryItem(title = "🔘 Buttons", factory = buttonsStoryFactory),
StoryItem(title = "🏷️ Badge", factory = tangemBadgeStoryFactory),
StoryItem(title = "✨ Opportunities BG", factory = opportunitiesBGStoryFactory),

View file

@ -6,6 +6,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.feature.tester.presentation.storybook.entity.ButtonsStory
import com.tangem.feature.tester.presentation.storybook.entity.DeviceIconStory
import com.tangem.feature.tester.presentation.storybook.entity.DsComponentsListStory
import com.tangem.feature.tester.presentation.storybook.entity.NorthernLightsStory
import com.tangem.feature.tester.presentation.storybook.entity.OpportunitiesBGStory
import com.tangem.feature.tester.presentation.storybook.entity.PlaceholderStory
@ -15,6 +16,7 @@ import com.tangem.feature.tester.presentation.storybook.entity.StoryBookUM
import com.tangem.feature.tester.presentation.storybook.entity.StoryList
import com.tangem.feature.tester.presentation.storybook.entity.TangemCheckboxStory
import com.tangem.feature.tester.presentation.storybook.entity.TangemHeaderRowStory
import com.tangem.feature.tester.presentation.storybook.entity.TangemLoaderStory
import com.tangem.feature.tester.presentation.storybook.entity.TangemContextMenuStory
import com.tangem.feature.tester.presentation.storybook.entity.TangemMessageStory
import com.tangem.feature.tester.presentation.storybook.entity.TangemPagerIndicatorStory
@ -28,6 +30,8 @@ import com.tangem.feature.tester.presentation.storybook.page.background.Northern
import com.tangem.feature.tester.presentation.storybook.page.badge.TangemBadgeStory
import com.tangem.feature.tester.presentation.storybook.page.buttons.ButtonsStory
import com.tangem.feature.tester.presentation.storybook.page.deviceicon.DeviceIconStory
import com.tangem.feature.tester.presentation.storybook.page.ds.DsComponentsListStory
import com.tangem.feature.tester.presentation.storybook.page.ds.loader.TangemLoaderStory
import com.tangem.feature.tester.presentation.storybook.page.opportunities.OpportunitiesBGStory
import com.tangem.feature.tester.presentation.storybook.page.checkbox.TangemCheckboxStory
import com.tangem.feature.tester.presentation.storybook.page.message.TangemMessageStory
@ -73,6 +77,8 @@ internal fun StoryBookScreen(state: StoryBookUM, modifier: Modifier = Modifier)
PlaceholderStory -> PlaceholderStory()
ProgressIndicatorStory -> ProgressIndicatorStory()
DeviceIconStory -> DeviceIconStory()
is DsComponentsListStory -> DsComponentsListStory(state = storyState)
is TangemLoaderStory -> TangemLoaderStory(state = storyState)
}
}
}

View file

@ -2,9 +2,11 @@ package com.tangem.feature.tester.presentation.storybook.viewmodel
import androidx.lifecycle.ViewModel
import com.tangem.feature.tester.presentation.navigation.InnerTesterRouter
import com.tangem.feature.tester.presentation.storybook.entity.DsStoryBookPage
import com.tangem.feature.tester.presentation.storybook.entity.StoryBookUM
import com.tangem.feature.tester.presentation.storybook.entity.StoryList
import com.tangem.feature.tester.presentation.storybook.entity.StoryPageFactory
import com.tangem.feature.tester.presentation.storybook.page.ds.dsComponentsListStoryFactory
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -30,10 +32,10 @@ internal class StoryBookViewModel @Inject constructor() : ViewModel() {
}
private fun onBackClick() {
if (_uiState.value.currentPage !is StoryList) {
_uiState.update { it.copy(currentPage = StoryList) }
} else {
router?.back()
when (_uiState.value.currentPage) {
is StoryList -> router?.back()
is DsStoryBookPage -> onStoryClick(dsComponentsListStoryFactory)
else -> _uiState.update { it.copy(currentPage = StoryList) }
}
}