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
pull/1732/head
Jaehwa Noh 2 weeks ago committed by GitHub
parent 90b992b950
commit 48544194f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -18,7 +18,8 @@ package com.google.samples.apps.nowinandroid.ui
import androidx.compose.ui.semantics.SemanticsActions.ScrollBy import androidx.compose.ui.semantics.SemanticsActions.ScrollBy
import androidx.compose.ui.test.assertCountEquals import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsOn import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertIsSelected import androidx.compose.ui.test.assertIsSelected
import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.hasText import androidx.compose.ui.test.hasText
@ -87,6 +88,8 @@ class NavigationTest {
private val forYou by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_title) private val forYou by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_title)
private val interests by composeTestRule.stringResource(FeatureSearchR.string.feature_search_interests) private val interests by composeTestRule.stringResource(FeatureSearchR.string.feature_search_interests)
private val sampleTopic = "Headlines" private val sampleTopic = "Headlines"
private val sampleTopicCheckIconDescription = "Headlines checked"
private val sampleTopicAddIconDescription = "Headlines add"
private val appName by composeTestRule.stringResource(R.string.app_name) private val appName by composeTestRule.stringResource(R.string.app_name)
private val saved by composeTestRule.stringResource(BookmarksR.string.feature_bookmarks_title) private val saved by composeTestRule.stringResource(BookmarksR.string.feature_bookmarks_title)
private val settings by composeTestRule.stringResource(SettingsR.string.feature_settings_top_app_bar_action_icon_description) private val settings by composeTestRule.stringResource(SettingsR.string.feature_settings_top_app_bar_action_icon_description)
@ -115,13 +118,20 @@ class NavigationTest {
fun navigationBar_navigateToPreviouslySelectedTab_restoresContent() { fun navigationBar_navigateToPreviouslySelectedTab_restoresContent() {
composeTestRule.apply { composeTestRule.apply {
// GIVEN the user follows a topic // GIVEN the user follows a topic
onNodeWithText(sampleTopic).performClick() onNodeWithContentDescription(sampleTopic).performClick()
// WHEN the user navigates to the Interests destination // WHEN the user navigates to the Interests destination
onNodeWithText(interests).performClick() onNodeWithText(interests).performClick()
// AND the user navigates to the For You destination // AND the user navigates to the For You destination
onNodeWithText(forYou).performClick() onNodeWithText(forYou).performClick()
// THEN the state of the For You destination is restored // THEN the state of the For You destination is restored
onNodeWithContentDescription(sampleTopic).assertIsOn() onNodeWithContentDescription(
sampleTopicCheckIconDescription,
useUnmergedTree = true,
).assertIsDisplayed()
onNodeWithContentDescription(
sampleTopicAddIconDescription,
useUnmergedTree = true,
).assertIsNotDisplayed()
} }
} }
@ -132,11 +142,18 @@ class NavigationTest {
fun navigationBar_reselectTab_keepsState() { fun navigationBar_reselectTab_keepsState() {
composeTestRule.apply { composeTestRule.apply {
// GIVEN the user follows a topic // GIVEN the user follows a topic
onNodeWithText(sampleTopic).performClick() onNodeWithContentDescription(sampleTopic).performClick()
// WHEN the user taps the For You navigation bar item // WHEN the user taps the For You navigation bar item
onNodeWithText(forYou).performClick() onNodeWithText(forYou).performClick()
// THEN the state of the For You destination is restored // THEN the state of the For You destination is restored
onNodeWithContentDescription(sampleTopic).assertIsOn() onNodeWithContentDescription(
sampleTopicCheckIconDescription,
useUnmergedTree = true,
).assertIsDisplayed()
onNodeWithContentDescription(
sampleTopicAddIconDescription,
useUnmergedTree = true,
).assertIsNotDisplayed()
} }
} }

@ -128,7 +128,7 @@ class ForYouScreenTest {
testData.forEach { testTopic -> testData.forEach { testTopic ->
composeTestRule composeTestRule
.onNodeWithText(testTopic.topic.name) .onNodeWithContentDescription(testTopic.topic.name)
.assertExists() .assertExists()
.assertHasClickAction() .assertHasClickAction()
} }
@ -175,7 +175,7 @@ class ForYouScreenTest {
followableTopicTestData.forEach { testTopic -> followableTopicTestData.forEach { testTopic ->
composeTestRule composeTestRule
.onNodeWithText(testTopic.topic.name) .onNodeWithContentDescription(testTopic.topic.name)
.assertExists() .assertExists()
.assertHasClickAction() .assertHasClickAction()
} }

@ -75,6 +75,13 @@ import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.semantics.toggleableState
import androidx.compose.ui.state.ToggleableState.Off
import androidx.compose.ui.state.ToggleableState.On
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -268,23 +275,30 @@ private fun LazyStaggeredGridScope.onboarding(
is OnboardingUiState.Shown -> { is OnboardingUiState.Shown -> {
item(span = StaggeredGridItemSpan.FullLine, contentType = "onboarding") { item(span = StaggeredGridItemSpan.FullLine, contentType = "onboarding") {
Column(modifier = interestsItemModifier) { Column(
Text( modifier = interestsItemModifier,
text = stringResource(R.string.feature_foryou_onboarding_guidance_title), ) {
textAlign = TextAlign.Center, Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .semantics(mergeDescendants = true) { },
.padding(top = 24.dp), ) {
style = MaterialTheme.typography.titleMedium, Text(
) text = stringResource(R.string.feature_foryou_onboarding_guidance_title),
Text( textAlign = TextAlign.Center,
text = stringResource(R.string.feature_foryou_onboarding_guidance_subtitle), modifier = Modifier
modifier = Modifier .fillMaxWidth()
.fillMaxWidth() .padding(top = 24.dp),
.padding(top = 8.dp, start = 24.dp, end = 24.dp), style = MaterialTheme.typography.titleMedium,
textAlign = TextAlign.Center, )
style = MaterialTheme.typography.bodyMedium, Text(
) text = stringResource(R.string.feature_foryou_onboarding_guidance_subtitle),
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp, start = 24.dp, end = 24.dp),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyMedium,
)
}
TopicSelection( TopicSelection(
onboardingUiState, onboardingUiState,
onTopicCheckedChanged, onTopicCheckedChanged,
@ -384,7 +398,21 @@ private fun SingleTopicButton(
Surface( Surface(
modifier = Modifier modifier = Modifier
.width(312.dp) .width(312.dp)
.heightIn(min = 56.dp), .heightIn(min = 56.dp)
.semantics(mergeDescendants = true) {
toggleableState = if (isSelected) {
On
} else {
Off
}
stateDescription = if (isSelected) {
"Following"
} else {
"Not Following"
}
contentDescription = name
},
shape = RoundedCornerShape(corner = CornerSize(8.dp)), shape = RoundedCornerShape(corner = CornerSize(8.dp)),
color = MaterialTheme.colorScheme.surface, color = MaterialTheme.colorScheme.surface,
selected = isSelected, selected = isSelected,
@ -394,7 +422,9 @@ private fun SingleTopicButton(
) { ) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(start = 12.dp, end = 8.dp), modifier = Modifier
.padding(start = 12.dp, end = 8.dp)
.clearAndSetSemantics { },
) { ) {
TopicIcon( TopicIcon(
imageUrl = imageUrl, imageUrl = imageUrl,
@ -413,13 +443,19 @@ private fun SingleTopicButton(
icon = { icon = {
Icon( Icon(
imageVector = NiaIcons.Add, imageVector = NiaIcons.Add,
contentDescription = name, contentDescription = stringResource(
id = R.string.feature_foryou_topic_icon_add,
name,
),
) )
}, },
checkedIcon = { checkedIcon = {
Icon( Icon(
imageVector = NiaIcons.Check, imageVector = NiaIcons.Check,
contentDescription = name, contentDescription = stringResource(
id = R.string.feature_foryou_topic_icon_checked,
name,
),
) )
}, },
) )

@ -21,5 +21,7 @@
<string name="feature_foryou_navigate_up">Navigate up</string> <string name="feature_foryou_navigate_up">Navigate up</string>
<string name="feature_foryou_onboarding_guidance_title">What are you interested in?</string> <string name="feature_foryou_onboarding_guidance_title">What are you interested in?</string>
<string name="feature_foryou_onboarding_guidance_subtitle">Updates from topics you follow will appear here. Follow some things to get started.</string> <string name="feature_foryou_onboarding_guidance_subtitle">Updates from topics you follow will appear here. Follow some things to get started.</string>
<string name="feature_foryou_topic_icon_checked">%s checked</string>
<string name="feature_foryou_topic_icon_add">%s add</string>
</resources> </resources>

Loading…
Cancel
Save