Skip to content

Commit 595d4fd

Browse files
feat: Add Error screen and loading screen demos to test wrapper
1 parent cf3c1af commit 595d4fd

9 files changed

Lines changed: 376 additions & 12 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package uk.gov.android.ui.patterns.loadingscreen
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Arrangement
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.fillMaxSize
7+
import androidx.compose.foundation.layout.padding
8+
import androidx.compose.material3.CircularProgressIndicator
9+
import androidx.compose.material3.MaterialTheme
10+
import androidx.compose.material3.Text
11+
import androidx.compose.runtime.Composable
12+
import androidx.compose.runtime.remember
13+
import androidx.compose.ui.Alignment
14+
import androidx.compose.ui.Modifier
15+
import androidx.compose.ui.focus.FocusRequester
16+
import androidx.compose.ui.focus.focusRequester
17+
import androidx.compose.ui.res.stringResource
18+
import androidx.compose.ui.semantics.clearAndSetSemantics
19+
import androidx.compose.ui.semantics.contentDescription
20+
import androidx.compose.ui.semantics.hideFromAccessibility
21+
import androidx.compose.ui.semantics.semantics
22+
import androidx.compose.ui.semantics.text
23+
import androidx.compose.ui.text.AnnotatedString
24+
import androidx.compose.ui.text.style.TextAlign
25+
import androidx.compose.ui.tooling.preview.PreviewLightDark
26+
import androidx.compose.ui.tooling.preview.PreviewParameter
27+
import uk.gov.android.ui.patterns.R
28+
import uk.gov.android.ui.theme.largePadding
29+
import uk.gov.android.ui.theme.m3.GdsLocalColorScheme
30+
import uk.gov.android.ui.theme.m3.GdsTheme
31+
import uk.gov.android.ui.theme.util.UnstableDesignSystemAPI
32+
33+
/**
34+
* Loading screen
35+
*
36+
* A composable that displays a loading indicator [CircularProgressIndicator] and a text message.
37+
* This is typically used to indicate that a background operation is in progress.
38+
*
39+
* @param text The message to display (e.g., "Loading").
40+
* @param modifier Modifier applied to the root container of the loading screen.
41+
*
42+
*/
43+
@UnstableDesignSystemAPI
44+
@Composable
45+
fun LoadingScreenDynamic(
46+
modifier: Modifier = Modifier,
47+
text: String = stringResource(R.string.loading),
48+
) {
49+
val focusRequester = remember { FocusRequester() }
50+
Column(
51+
modifier = modifier
52+
.fillMaxSize()
53+
.background(MaterialTheme.colorScheme.background),
54+
verticalArrangement = Arrangement.Center,
55+
horizontalAlignment = Alignment.CenterHorizontally,
56+
) {
57+
CircularProgressIndicator(
58+
color = GdsLocalColorScheme.current.spinnerIcon,
59+
modifier = Modifier.semantics { hideFromAccessibility() },
60+
)
61+
Text(
62+
text = text,
63+
color = MaterialTheme.colorScheme.onBackground,
64+
style = MaterialTheme.typography.bodyLarge,
65+
modifier = Modifier
66+
.padding(top = largePadding)
67+
.clearAndSetSemantics {
68+
this.contentDescription = text
69+
this.text = AnnotatedString(text)
70+
}
71+
.focusRequester(focusRequester),
72+
textAlign = TextAlign.Center,
73+
)
74+
}
75+
}
76+
77+
@OptIn(UnstableDesignSystemAPI::class)
78+
@PreviewLightDark
79+
@Composable
80+
private fun PreviewDefaultLoadingScreen(
81+
@PreviewParameter(LoadingScreenContentProvider::class)
82+
content: String,
83+
) {
84+
GdsTheme {
85+
LoadingScreen(text = content)
86+
}
87+
}
88+
89+
@OptIn(UnstableDesignSystemAPI::class)
90+
@PreviewLightDark
91+
@Composable
92+
private fun PreviewDefaultLoadingScreenNoContent() {
93+
GdsTheme {
94+
LoadingScreen()
95+
}
96+
}

testwrapper/src/main/java/uk/gov/android/ui/testwrapper/AppNavHost.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ fun AppNavHost(
2121
navController: NavHostController,
2222
startDestination: Any,
2323
modifier: Modifier = Modifier,
24-
displayTabRow: (Boolean) -> Unit = {}
24+
displayTabRow: (Boolean) -> Unit = {},
2525
) {
2626
val tabPagesOffsetPadding = 50.dp
2727
val scope = rememberCoroutineScope()

testwrapper/src/main/java/uk/gov/android/ui/testwrapper/PatternListDetail.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import uk.gov.android.ui.testwrapper.patterns.PatternDetail
99
fun PatternListDetail(
1010
items: ImmutableList<DetailItem>,
1111
modifier: Modifier = Modifier,
12-
displayTabRow: (Boolean) -> Unit
12+
displayTabRow: (Boolean) -> Unit,
1313
) {
1414
ListDetail(
1515
items = items,
1616
detail = { detailItem ->
1717
PatternDetail(
1818
detailItem,
19-
displayTabRow
19+
displayTabRow,
2020
)
2121
},
2222
modifier = modifier,

testwrapper/src/main/java/uk/gov/android/ui/testwrapper/patterns/Patterns.kt

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package uk.gov.android.ui.testwrapper.patterns
22

3-
import androidx.activity.compose.BackHandler
43
import androidx.compose.foundation.clickable
54
import androidx.compose.foundation.layout.fillMaxSize
65
import androidx.compose.foundation.layout.fillMaxWidth
@@ -17,7 +16,10 @@ import uk.gov.android.ui.componentsv2.heading.GdsHeadingAlignment
1716
import uk.gov.android.ui.componentsv2.heading.GdsHeadingStyle
1817
import uk.gov.android.ui.patterns.loadingscreen.LoadingScreen
1918
import uk.gov.android.ui.testwrapper.DetailItem
19+
import uk.gov.android.ui.testwrapper.patterns.error.ErrorScreenDemo
20+
import uk.gov.android.ui.testwrapper.patterns.error.ErrorScreenDeprecatedDemo
2021
import uk.gov.android.ui.testwrapper.patterns.loading.LoadingFullScreenDemo
22+
import uk.gov.android.ui.testwrapper.patterns.loading.LoadingScreenMultipleValuesDemo
2123
import uk.gov.android.ui.theme.smallPadding
2224
import uk.gov.android.ui.theme.util.UnstableDesignSystemAPI
2325

@@ -51,7 +53,7 @@ fun Patterns(
5153
@Composable
5254
fun PatternDetail(
5355
detailItem: DetailItem,
54-
displayTabRow: (Boolean) -> Unit
56+
displayTabRow: (Boolean) -> Unit,
5557
) {
5658
when (detailItem.label) {
5759
LOADING_SCREEN -> LoadingScreen()
@@ -61,8 +63,35 @@ fun PatternDetail(
6163
}
6264
LoadingFullScreenDemo(displayTabRow)
6365
}
66+
ERROR_SCREEN -> ErrorScreenDemo(isFullScreen = false)
67+
ERROR_FULL_SCREEN -> {
68+
LaunchedEffect(Unit) {
69+
displayTabRow(false)
70+
}
71+
ErrorScreenDemo(isFullScreen = true, displayTabRow = displayTabRow)
72+
}
73+
ERROR_DEPRECATED_SCREEN -> ErrorScreenDeprecatedDemo(isFullScreen = false)
74+
ERROR_DEPRECATED_FULL_SCREEN -> {
75+
LaunchedEffect(Unit) {
76+
displayTabRow(false)
77+
}
78+
ErrorScreenDeprecatedDemo(isFullScreen = true, displayTabRow = displayTabRow)
79+
}
80+
LOADING_DYNAMIC_SCREEN -> LoadingScreenMultipleValuesDemo(displayTabRow)
81+
LOADING_DYNAMIC_FULL_SCREEN -> {
82+
LaunchedEffect(Unit) {
83+
displayTabRow(false)
84+
}
85+
LoadingScreenMultipleValuesDemo(displayTabRow)
86+
}
6487
}
6588
}
6689

6790
const val LOADING_SCREEN = "loadingScreen"
68-
const val LOADING_FULL_SCREEN = "fullLoadingScreen"
91+
const val LOADING_FULL_SCREEN = "fullLoadingScreen"
92+
const val ERROR_SCREEN = "errorScreen"
93+
const val ERROR_FULL_SCREEN = "fullErrorScreen"
94+
const val ERROR_DEPRECATED_SCREEN = "errorDeprecatedScreen"
95+
const val ERROR_DEPRECATED_FULL_SCREEN = "fullErrorDeprecatedScreen"
96+
const val LOADING_DYNAMIC_SCREEN = "loadingDynamicScreen"
97+
const val LOADING_DYNAMIC_FULL_SCREEN = "fullLoadingDynamicScreen"

testwrapper/src/main/java/uk/gov/android/ui/testwrapper/patterns/PatternsDestination.kt

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package uk.gov.android.ui.testwrapper.patterns
22

33
import androidx.compose.foundation.layout.padding
4-
import androidx.compose.runtime.LaunchedEffect
54
import androidx.compose.ui.Modifier
65
import androidx.navigation.NavGraphBuilder
76
import androidx.navigation.compose.composable
@@ -49,8 +48,8 @@ sealed class PatternsDestination(
4948
companion object {
5049
fun NavGraphBuilder.applyPatternDestinations(
5150
modifier: Modifier = Modifier,
52-
displayTabRow: (Boolean) -> Unit
53-
) {
51+
displayTabRow: (Boolean) -> Unit,
52+
) {
5453
composable<Placeholder> { navBackStackEntry ->
5554
val arguments: Placeholder = navBackStackEntry.toRoute()
5655
Placeholder(
@@ -63,7 +62,7 @@ sealed class PatternsDestination(
6362
PatternListDetail(
6463
items = arguments.items.toPersistentList(),
6564
modifier = modifier,
66-
displayTabRow = displayTabRow
65+
displayTabRow = displayTabRow,
6766
)
6867
}
6968
}
@@ -72,14 +71,28 @@ sealed class PatternsDestination(
7271
listOf(
7372
Placeholder(text = "Center Aligned Screen"),
7473
Placeholder(text = "Dialog"),
75-
Placeholder(text = "Error Screen"),
74+
DetailedItem(
75+
text = "Error Screen",
76+
items =
77+
listOf(
78+
DetailItem(label = ERROR_SCREEN, name = "Error Screen"),
79+
DetailItem(label = ERROR_FULL_SCREEN, name = "Error Full Screen"),
80+
DetailItem(label = ERROR_SCREEN, name = "Error Screen (Deprecated)"),
81+
DetailItem(
82+
label = ERROR_FULL_SCREEN,
83+
name = "Error Full Screen (Deprecated)",
84+
),
85+
),
86+
),
7687
Placeholder(text = "Left Aligned Screen"),
7788
DetailedItem(
7889
text = "Loading Screen",
7990
items =
8091
listOf(
8192
DetailItem(label = LOADING_SCREEN, name = "Loading Screen"),
8293
DetailItem(label = LOADING_FULL_SCREEN, name = "Loading Full Screen"),
94+
DetailItem(label = LOADING_DYNAMIC_SCREEN, name = "Loading Dynamic Screen"),
95+
DetailItem(label = LOADING_DYNAMIC_FULL_SCREEN, name = "Loading Dynamic Full Screen"),
8396
),
8497
),
8598
// Add new demo items here
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package uk.gov.android.ui.testwrapper.patterns.error
2+
3+
import androidx.activity.compose.BackHandler
4+
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
5+
import androidx.compose.foundation.layout.PaddingValues
6+
import androidx.compose.foundation.layout.fillMaxWidth
7+
import androidx.compose.foundation.layout.padding
8+
import androidx.compose.foundation.lazy.LazyListScope
9+
import androidx.compose.material3.MaterialTheme.colorScheme
10+
import androidx.compose.material3.Text
11+
import androidx.compose.runtime.Composable
12+
import androidx.compose.runtime.getValue
13+
import androidx.compose.runtime.mutableStateOf
14+
import androidx.compose.runtime.remember
15+
import androidx.compose.runtime.rememberCoroutineScope
16+
import androidx.compose.runtime.setValue
17+
import androidx.compose.ui.Modifier
18+
import androidx.compose.ui.graphics.vector.ImageVector
19+
import androidx.compose.ui.res.stringResource
20+
import androidx.compose.ui.res.vectorResource
21+
import androidx.compose.ui.text.font.FontWeight
22+
import androidx.compose.ui.text.style.TextAlign
23+
import androidx.compose.ui.unit.Dp
24+
import kotlinx.collections.immutable.ImmutableList
25+
import kotlinx.collections.immutable.persistentListOf
26+
import kotlinx.coroutines.android.awaitFrame
27+
import kotlinx.coroutines.launch
28+
import uk.gov.android.ui.componentsv2.button.ButtonTypeV2
29+
import uk.gov.android.ui.componentsv2.button.GdsButton
30+
import uk.gov.android.ui.componentsv2.heading.GdsHeading
31+
import uk.gov.android.ui.componentsv2.heading.GdsHeadingAlignment
32+
import uk.gov.android.ui.componentsv2.images.GdsIcon
33+
import uk.gov.android.ui.patterns.errorscreen.v2.ErrorScreen
34+
import uk.gov.android.ui.patterns.errorscreen.v2.ErrorScreenBodyContent
35+
import uk.gov.android.ui.patterns.errorscreen.v2.ErrorScreenIcon
36+
import uk.gov.android.ui.theme.m3.Typography
37+
38+
@Composable
39+
@Suppress("ComposeModifierMissing")
40+
fun ErrorScreenDemo(
41+
isFullScreen: Boolean,
42+
displayTabRow: (Boolean) -> Unit = {},
43+
) {
44+
if (isFullScreen) {
45+
val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
46+
var backPressHandled by remember { mutableStateOf(false) }
47+
val coroutineScope = rememberCoroutineScope()
48+
BackHandler(enabled = !backPressHandled) {
49+
backPressHandled = true
50+
displayTabRow(true)
51+
coroutineScope.launch {
52+
awaitFrame()
53+
onBackPressedDispatcher?.onBackPressed()
54+
backPressHandled = false
55+
}
56+
}
57+
}
58+
ErrorScreen(
59+
icon = { horizontalPadding ->
60+
GdsIcon(
61+
image = ImageVector.vectorResource(ErrorScreenIcon.ErrorIcon.icon),
62+
contentDescription = stringResource(ErrorScreenIcon.ErrorIcon.description),
63+
modifier = Modifier
64+
.fillMaxWidth()
65+
.padding(horizontal = horizontalPadding),
66+
color = colorScheme.onBackground,
67+
)
68+
},
69+
title = { horizontalPadding ->
70+
GdsHeading(
71+
text = "This is an Error View title",
72+
modifier = Modifier
73+
.padding(horizontal = horizontalPadding),
74+
textAlign = GdsHeadingAlignment.CenterAligned,
75+
)
76+
},
77+
body = { horizontalPadding ->
78+
toBodyContent(
79+
persistentListOf(
80+
ErrorScreenBodyContent.Text("Body single line (regular)"),
81+
ErrorScreenBodyContent.Text("Body single line (bold)", true),
82+
),
83+
horizontalPadding,
84+
)
85+
},
86+
primaryButton = {
87+
GdsButton(
88+
text = "Primary button",
89+
buttonType = ButtonTypeV2.Primary(),
90+
onClick = {},
91+
modifier = Modifier.fillMaxWidth(),
92+
)
93+
},
94+
)
95+
}
96+
97+
internal fun LazyListScope.toBodyContent(
98+
body: ImmutableList<ErrorScreenBodyContent.Text>? = null,
99+
horizontalItemPadding: Dp,
100+
) {
101+
val itemPadding = PaddingValues(horizontal = horizontalItemPadding)
102+
body?.forEach { item ->
103+
item {
104+
val textStyle = if (item.useBoldStyle) {
105+
Typography.bodyLarge.copy(fontWeight = FontWeight.Bold)
106+
} else {
107+
Typography.bodyLarge
108+
}
109+
Text(
110+
text = item.bodyText,
111+
style = textStyle,
112+
color = colorScheme.onBackground,
113+
textAlign = TextAlign.Center,
114+
modifier = Modifier
115+
.fillMaxWidth()
116+
.padding(itemPadding),
117+
)
118+
}
119+
}
120+
}

0 commit comments

Comments
 (0)