This commit addresses the accessibility issues described in Issue 296: 1. Replaced `pointerInput` gestures with `combinedClickable(role = Role.Button)` in `ChatBubble.kt` and `ChatScreen.kt` to ensure screen readers announce interactive text elements and chat bubbles correctly. 2. Added `role = Role.Button` to the clickable modifiers in `SessionsScreen.kt`. 3. Introduced proper `testTag` modifiers throughout interactive components like `session_card`, `chat_bubble_user`, `chat_bubble_assistant`, and `clarify_option` to facilitate UI testing.
This commit is contained in:
3 files changed
+62
-34
No files matched your search
@@ -10,8 +10,10 @@ import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -51,11 +53,12 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
@@ -126,6 +129,7 @@ fun ChatBubble(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun UserBubble(
|
||||
message: ChatMessage,
|
||||
@@ -198,13 +202,14 @@ private fun UserBubble(
|
||||
bottomEnd = 4.dp,
|
||||
),
|
||||
).background(brush = gradientBrush)
|
||||
.pointerInput(message.content) {
|
||||
detectTapGestures(
|
||||
onLongPress = {
|
||||
showCopyButton = true
|
||||
},
|
||||
)
|
||||
},
|
||||
.testTag("chat_bubble_user")
|
||||
.combinedClickable(
|
||||
role = Role.Button,
|
||||
onClick = {},
|
||||
onLongClick = {
|
||||
showCopyButton = true
|
||||
},
|
||||
),
|
||||
color = Color.Transparent,
|
||||
tonalElevation = 0.dp,
|
||||
) {
|
||||
@@ -265,6 +270,7 @@ private fun UserBubble(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun AssistantBubble(
|
||||
message: ChatMessage,
|
||||
@@ -320,13 +326,14 @@ private fun AssistantBubble(
|
||||
bottomStart = 16.dp,
|
||||
bottomEnd = 16.dp,
|
||||
),
|
||||
).pointerInput(message.content) {
|
||||
detectTapGestures(
|
||||
onLongPress = {
|
||||
showCopyButton = true
|
||||
},
|
||||
)
|
||||
},
|
||||
).testTag("chat_bubble_assistant")
|
||||
.combinedClickable(
|
||||
role = Role.Button,
|
||||
onClick = {},
|
||||
onLongClick = {
|
||||
showCopyButton = true
|
||||
},
|
||||
),
|
||||
color = bubbleColor,
|
||||
border =
|
||||
BorderStroke(
|
||||
@@ -677,9 +684,9 @@ private fun ToolBubble(
|
||||
textDecoration = TextDecoration.Underline,
|
||||
),
|
||||
modifier =
|
||||
Modifier.pointerInput(Unit) {
|
||||
detectTapGestures(onTap = { showRawJson = true })
|
||||
},
|
||||
Modifier
|
||||
.testTag("chat_tool_show_raw")
|
||||
.clickable(role = Role.Button) { showRawJson = true },
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
@@ -702,9 +709,9 @@ private fun ToolBubble(
|
||||
textDecoration = TextDecoration.Underline,
|
||||
),
|
||||
modifier =
|
||||
Modifier.pointerInput(Unit) {
|
||||
detectTapGestures(onTap = { showRawJson = false })
|
||||
},
|
||||
Modifier
|
||||
.testTag("chat_tool_show_parsed")
|
||||
.clickable(role = Role.Button) { showRawJson = false },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,10 @@ import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -68,6 +69,7 @@ import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.material3.Snackbar
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -88,10 +90,10 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
@@ -111,6 +113,7 @@ import com.m57.hermescontrol.ui.common.HermesScaffold
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun ChatScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -832,6 +835,7 @@ private fun ChatInputBar(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun ClarifyDialog(
|
||||
clarify: ClarifyUi,
|
||||
@@ -856,20 +860,34 @@ private fun ClarifyDialog(
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
clarify.options.forEach { option ->
|
||||
FilledTonalButton(
|
||||
onClick = { /* onTap handled by pointerInput */ },
|
||||
Surface(
|
||||
onClick = { typedText = option },
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.pointerInput(option) {
|
||||
detectTapGestures(
|
||||
onTap = { typedText = option },
|
||||
onLongPress = { onOptionSelected(option) },
|
||||
)
|
||||
},
|
||||
.testTag("clarify_option"),
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
color = MaterialTheme.colorScheme.secondaryContainer,
|
||||
) {
|
||||
Text(option)
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.combinedClickable(
|
||||
role = Role.Button,
|
||||
onClick = { typedText = option },
|
||||
onLongClick = { onOptionSelected(option) },
|
||||
)
|
||||
.padding(horizontal = 24.dp, vertical = 10.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
text = option,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,9 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
@@ -108,7 +110,8 @@ fun SessionsScreen(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
.testTag("session_card_${session.id}")
|
||||
.clickable(role = Role.Button) {
|
||||
NavigationController.pendingSessionId = session.id
|
||||
NavigationController.navigateTo(ChatScreen)
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user