Skip to content

Commit 4854419

Browse files
authored
Improve accessibility to ForYou topics (#1309)
* Clear semantics. Change-Id: I69a48798f6be8433e877c38166bd73cd776d71ac * Merge the descendants semantics. Change-Id: Ifbe91bb694b393e6780f93732a7d38f5ee355680 * Add string for icon description. Change-Id: Ibf35c651aeacfbe317794ed898f0fb0e8bc1ec15 * Change icon description. Change-Id: I9359bf8c9272b247669ae58fd79e5f79466ec0a9 * Change state of icon check logic. Change-Id: I3e5e89fc1c469c3f3c4744d18f697f68d070996b * Merge only text. Change-Id: I36eefc6dd9f56f7a1f63003f0f95f6755ae16def * Add stateDescription. Change-Id: Id4d32e49374dbdb6e666048937e37060cbd4b454 * Add toggleableState. Change-Id: I9f836e2463bf800786d11f9e0997a7d570ff90b0 * Set clearAndSetSemantics at Row. Change-Id: Ic25b9ca11bbcc40c8ff24172669b07ea9b7642a9 * Set stateDescription "Following" and "Not Following". Change-Id: Ib82ac94b05a5d373ac10787aac5241fb4f03c7b2 * Find topic by onNodeWithContentDescription. Change-Id: I2423e3846c02169f6bf1fc30d9b4034b5f79efb3 * Find topic by onNodeWithContentDescription. Change-Id: Iefc97fd9e52227f51f7d40f8c10cb4878cde90c0
1 parent 90b992b commit 4854419

File tree

4 files changed

+82
-27
lines changed

4 files changed

+82
-27
lines changed

app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ package com.google.samples.apps.nowinandroid.ui
1818

1919
import androidx.compose.ui.semantics.SemanticsActions.ScrollBy
2020
import androidx.compose.ui.test.assertCountEquals
21-
import androidx.compose.ui.test.assertIsOn
21+
import androidx.compose.ui.test.assertIsDisplayed
22+
import androidx.compose.ui.test.assertIsNotDisplayed
2223
import androidx.compose.ui.test.assertIsSelected
2324
import androidx.compose.ui.test.hasTestTag
2425
import androidx.compose.ui.test.hasText
@@ -87,6 +88,8 @@ class NavigationTest {
8788
private val forYou by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_title)
8889
private val interests by composeTestRule.stringResource(FeatureSearchR.string.feature_search_interests)
8990
private val sampleTopic = "Headlines"
91+
private val sampleTopicCheckIconDescription = "Headlines checked"
92+
private val sampleTopicAddIconDescription = "Headlines add"
9093
private val appName by composeTestRule.stringResource(R.string.app_name)
9194
private val saved by composeTestRule.stringResource(BookmarksR.string.feature_bookmarks_title)
9295
private val settings by composeTestRule.stringResource(SettingsR.string.feature_settings_top_app_bar_action_icon_description)
@@ -115,13 +118,20 @@ class NavigationTest {
115118
fun navigationBar_navigateToPreviouslySelectedTab_restoresContent() {
116119
composeTestRule.apply {
117120
// GIVEN the user follows a topic
118-
onNodeWithText(sampleTopic).performClick()
121+
onNodeWithContentDescription(sampleTopic).performClick()
119122
// WHEN the user navigates to the Interests destination
120123
onNodeWithText(interests).performClick()
121124
// AND the user navigates to the For You destination
122125
onNodeWithText(forYou).performClick()
123126
// THEN the state of the For You destination is restored
124-
onNodeWithContentDescription(sampleTopic).assertIsOn()
127+
onNodeWithContentDescription(
128+
sampleTopicCheckIconDescription,
129+
useUnmergedTree = true,
130+
).assertIsDisplayed()
131+
onNodeWithContentDescription(
132+
sampleTopicAddIconDescription,
133+
useUnmergedTree = true,
134+
).assertIsNotDisplayed()
125135
}
126136
}
127137

@@ -132,11 +142,18 @@ class NavigationTest {
132142
fun navigationBar_reselectTab_keepsState() {
133143
composeTestRule.apply {
134144
// GIVEN the user follows a topic
135-
onNodeWithText(sampleTopic).performClick()
145+
onNodeWithContentDescription(sampleTopic).performClick()
136146
// WHEN the user taps the For You navigation bar item
137147
onNodeWithText(forYou).performClick()
138148
// THEN the state of the For You destination is restored
139-
onNodeWithContentDescription(sampleTopic).assertIsOn()
149+
onNodeWithContentDescription(
150+
sampleTopicCheckIconDescription,
151+
useUnmergedTree = true,
152+
).assertIsDisplayed()
153+
onNodeWithContentDescription(
154+
sampleTopicAddIconDescription,
155+
useUnmergedTree = true,
156+
).assertIsNotDisplayed()
140157
}
141158
}
142159

feature/foryou/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class ForYouScreenTest {
128128

129129
testData.forEach { testTopic ->
130130
composeTestRule
131-
.onNodeWithText(testTopic.topic.name)
131+
.onNodeWithContentDescription(testTopic.topic.name)
132132
.assertExists()
133133
.assertHasClickAction()
134134
}
@@ -175,7 +175,7 @@ class ForYouScreenTest {
175175

176176
followableTopicTestData.forEach { testTopic ->
177177
composeTestRule
178-
.onNodeWithText(testTopic.topic.name)
178+
.onNodeWithContentDescription(testTopic.topic.name)
179179
.assertExists()
180180
.assertHasClickAction()
181181
}

feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ import androidx.compose.ui.platform.LocalInspectionMode
7575
import androidx.compose.ui.platform.testTag
7676
import androidx.compose.ui.res.painterResource
7777
import androidx.compose.ui.res.stringResource
78+
import androidx.compose.ui.semantics.clearAndSetSemantics
79+
import androidx.compose.ui.semantics.contentDescription
80+
import androidx.compose.ui.semantics.semantics
81+
import androidx.compose.ui.semantics.stateDescription
82+
import androidx.compose.ui.semantics.toggleableState
83+
import androidx.compose.ui.state.ToggleableState.Off
84+
import androidx.compose.ui.state.ToggleableState.On
7885
import androidx.compose.ui.text.style.TextAlign
7986
import androidx.compose.ui.tooling.preview.PreviewParameter
8087
import androidx.compose.ui.unit.dp
@@ -268,23 +275,30 @@ private fun LazyStaggeredGridScope.onboarding(
268275

269276
is OnboardingUiState.Shown -> {
270277
item(span = StaggeredGridItemSpan.FullLine, contentType = "onboarding") {
271-
Column(modifier = interestsItemModifier) {
272-
Text(
273-
text = stringResource(R.string.feature_foryou_onboarding_guidance_title),
274-
textAlign = TextAlign.Center,
278+
Column(
279+
modifier = interestsItemModifier,
280+
) {
281+
Column(
275282
modifier = Modifier
276-
.fillMaxWidth()
277-
.padding(top = 24.dp),
278-
style = MaterialTheme.typography.titleMedium,
279-
)
280-
Text(
281-
text = stringResource(R.string.feature_foryou_onboarding_guidance_subtitle),
282-
modifier = Modifier
283-
.fillMaxWidth()
284-
.padding(top = 8.dp, start = 24.dp, end = 24.dp),
285-
textAlign = TextAlign.Center,
286-
style = MaterialTheme.typography.bodyMedium,
287-
)
283+
.semantics(mergeDescendants = true) { },
284+
) {
285+
Text(
286+
text = stringResource(R.string.feature_foryou_onboarding_guidance_title),
287+
textAlign = TextAlign.Center,
288+
modifier = Modifier
289+
.fillMaxWidth()
290+
.padding(top = 24.dp),
291+
style = MaterialTheme.typography.titleMedium,
292+
)
293+
Text(
294+
text = stringResource(R.string.feature_foryou_onboarding_guidance_subtitle),
295+
modifier = Modifier
296+
.fillMaxWidth()
297+
.padding(top = 8.dp, start = 24.dp, end = 24.dp),
298+
textAlign = TextAlign.Center,
299+
style = MaterialTheme.typography.bodyMedium,
300+
)
301+
}
288302
TopicSelection(
289303
onboardingUiState,
290304
onTopicCheckedChanged,
@@ -384,7 +398,21 @@ private fun SingleTopicButton(
384398
Surface(
385399
modifier = Modifier
386400
.width(312.dp)
387-
.heightIn(min = 56.dp),
401+
.heightIn(min = 56.dp)
402+
.semantics(mergeDescendants = true) {
403+
toggleableState = if (isSelected) {
404+
On
405+
} else {
406+
Off
407+
}
408+
stateDescription = if (isSelected) {
409+
"Following"
410+
} else {
411+
"Not Following"
412+
}
413+
414+
contentDescription = name
415+
},
388416
shape = RoundedCornerShape(corner = CornerSize(8.dp)),
389417
color = MaterialTheme.colorScheme.surface,
390418
selected = isSelected,
@@ -394,7 +422,9 @@ private fun SingleTopicButton(
394422
) {
395423
Row(
396424
verticalAlignment = Alignment.CenterVertically,
397-
modifier = Modifier.padding(start = 12.dp, end = 8.dp),
425+
modifier = Modifier
426+
.padding(start = 12.dp, end = 8.dp)
427+
.clearAndSetSemantics { },
398428
) {
399429
TopicIcon(
400430
imageUrl = imageUrl,
@@ -413,13 +443,19 @@ private fun SingleTopicButton(
413443
icon = {
414444
Icon(
415445
imageVector = NiaIcons.Add,
416-
contentDescription = name,
446+
contentDescription = stringResource(
447+
id = R.string.feature_foryou_topic_icon_add,
448+
name,
449+
),
417450
)
418451
},
419452
checkedIcon = {
420453
Icon(
421454
imageVector = NiaIcons.Check,
422-
contentDescription = name,
455+
contentDescription = stringResource(
456+
id = R.string.feature_foryou_topic_icon_checked,
457+
name,
458+
),
423459
)
424460
},
425461
)

feature/foryou/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,7 @@
2121
<string name="feature_foryou_navigate_up">Navigate up</string>
2222
<string name="feature_foryou_onboarding_guidance_title">What are you interested in?</string>
2323
<string name="feature_foryou_onboarding_guidance_subtitle">Updates from topics you follow will appear here. Follow some things to get started.</string>
24+
<string name="feature_foryou_topic_icon_checked">%s checked</string>
25+
<string name="feature_foryou_topic_icon_add">%s add</string>
2426

2527
</resources>

0 commit comments

Comments
 (0)