Coverage Summary for Class: SurferEmptyStateKt (com.georgeci.moneysurfer.uikit.components)

Class Class, % Method, % Branch, % Line, % Instruction, %
SurferEmptyStateKt 100% (1/1) 33.3% (1/3) 62.5% (15/24) 97.9% (47/48) 93.1% (512/550)


 package com.georgeci.moneysurfer.uikit.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.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.material3.Icon
 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.graphics.vector.ImageVector
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import com.georgeci.moneysurfer.uikit.icons.SurferIcons
 import com.georgeci.moneysurfer.uikit.preview.SurferComponentPreview
 import com.georgeci.moneysurfer.uikit.semantics.SurferSemantics
 import com.georgeci.moneysurfer.uikit.theme.AppTheme
 
 /**
  * Centered empty-state block: an icon medallion, a title, an optional subtitle and an
  * optional call-to-action. Use it when a list or screen has no content yet — the CTA is
  * what turns a dead end into the next step the user should take.
  *
  * To address the CTA from a UI test, tag the block through [modifier] at the call site — the
  * host screen owns its own selectors, and a dedicated tag parameter here would be one more
  * argument on an already wide signature.
  */
 @Composable
 fun SurferEmptyState(
     title: String,
     modifier: Modifier = Modifier,
     subtitle: String? = null,
     icon: ImageVector = SurferIcons.Info,
     actionLabel: String? = null,
     /** Leading glyph of the CTA. Defaults to `Add` — the common "create the first one" case. */
     actionIcon: ImageVector = SurferIcons.Add,
     onActionClick: (() -> Unit)? = null,
 ) {
     Column(
         modifier = modifier
             .fillMaxWidth()
             .padding(AppTheme.spacing.large),
         horizontalAlignment = Alignment.CenterHorizontally,
         verticalArrangement = Arrangement.spacedBy(AppTheme.spacing.small),
     ) {
         Box(
             modifier = Modifier
                 .size(64.dp)
                 .clip(CircleShape)
                 .background(AppTheme.materialColors.surfaceContainerHighest),
             contentAlignment = Alignment.Center,
         ) {
             Icon(
                 imageVector = icon,
                 contentDescription = SurferSemantics.Decorative,
                 tint = AppTheme.materialColors.onSurfaceVariant,
                 modifier = Modifier.size(28.dp),
             )
         }
         Text(
             text = title,
             style = AppTheme.typography.titleMedium,
             color = AppTheme.materialColors.onSurface,
             textAlign = TextAlign.Center,
         )
         if (subtitle != null) {
             Text(
                 text = subtitle,
                 style = AppTheme.typography.bodyMedium,
                 color = AppTheme.materialColors.onSurfaceVariant,
                 textAlign = TextAlign.Center,
             )
         }
         if (actionLabel != null && onActionClick != null) {
             SurferButton(
                 text = actionLabel,
                 onClick = onActionClick,
                 style = SurferButtonStyle.Tonal,
                 startIcon = actionIcon,
                 modifier = Modifier.padding(top = AppTheme.spacing.xSmall),
             )
         }
     }
 }
 
 @Preview
 @Composable
 private fun SurferEmptyStatePreview() {
     SurferComponentPreview {
         SurferEmptyState(
             title = "No activity yet",
             subtitle = "Transactions you log will show up here.",
             icon = SurferIcons.Receipt,
             actionLabel = "Add transaction",
             onActionClick = {},
         )
     }
 }
 
 @Preview
 @Composable
 private fun SurferEmptyStateNoActionPreview() {
     SurferComponentPreview {
         SurferEmptyState(
             title = "Nothing scheduled",
             subtitle = "Subscriptions and recurring bills will appear here.",
             icon = SurferIcons.Event,
         )
     }
 }