diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/DashboardScreen.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/DashboardScreen.kt index 417edda725..d47966ab44 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/DashboardScreen.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/DashboardScreen.kt @@ -199,7 +199,11 @@ fun DashboardScreen(uiState: DashboardUiState, mainNavController: NavHostControl refreshStateFlow ) HorizonSpace(SpaceSize.SPACE_16) - NumericWidgetRow(shouldRefresh, refreshStateFlow, homeNavController) + NumericWidgetRow( + shouldRefresh, + refreshStateFlow, + homeNavController + ) DashboardSkillHighlightsWidget( homeNavController, shouldRefresh, @@ -280,9 +284,12 @@ private fun HomeScreenTopBar(uiState: DashboardUiState, mainNavController: NavCo private fun NumericWidgetRow( shouldRefresh: Boolean, refreshStateFlow: MutableStateFlow>, - homeNavController: NavHostController + homeNavController: NavHostController, + modifier: Modifier = Modifier ) { - BoxWithConstraints { + BoxWithConstraints( + modifier = modifier + ) { val pageCount = 3 val pagerState = rememberPagerState{ pageCount } if (this.isWideLayout) { diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardWidgetCard.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardWidgetCard.kt index 904bc04b28..950cd838c5 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardWidgetCard.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardWidgetCard.kt @@ -23,7 +23,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -41,6 +40,7 @@ import androidx.compose.ui.res.painterResource 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.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -82,6 +82,10 @@ fun DashboardWidgetCard( contentDescription = context.getString(R.string.a11y_dashboardWidgetLoadingContentDescription, title) } + }.conditional(!isLoading) { + semantics { + contentDescription = "" + } }, onClick ) { diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/announcement/DashboardAnnouncementBannerWidget.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/announcement/DashboardAnnouncementBannerWidget.kt index 3ab7eb06a1..c44e3b238e 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/announcement/DashboardAnnouncementBannerWidget.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/announcement/DashboardAnnouncementBannerWidget.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.navigation.NavHostController @@ -38,7 +39,8 @@ fun DashboardAnnouncementBannerWidget( mainNavController: NavHostController, homeNavController: NavHostController, shouldRefresh: Boolean, - refreshState: MutableStateFlow> + refreshState: MutableStateFlow>, + modifier: Modifier = Modifier, ) { val viewModel = hiltViewModel() val state by viewModel.uiState.collectAsState() @@ -53,7 +55,7 @@ fun DashboardAnnouncementBannerWidget( } if (state.state != DashboardItemState.SUCCESS || state.cardState.items.isNotEmpty()) { - DashboardAnnouncementBannerSection(state, mainNavController, homeNavController) + DashboardAnnouncementBannerSection(state, mainNavController, homeNavController, modifier) } } @@ -62,6 +64,7 @@ fun DashboardAnnouncementBannerSection( state: DashboardAnnouncementBannerUiState, mainNavController: NavHostController, homeNavController: NavHostController, + modifier: Modifier = Modifier, ) { when (state.state) { DashboardItemState.LOADING -> { @@ -69,6 +72,7 @@ fun DashboardAnnouncementBannerSection( DashboardPaginatedWidgetCardState.Loading, mainNavController, homeNavController, + modifier ) } DashboardItemState.ERROR -> { @@ -79,6 +83,7 @@ fun DashboardAnnouncementBannerSection( false, DashboardWidgetPageState.Empty, { state.onRefresh {} }, + modifier ) } DashboardItemState.SUCCESS -> { @@ -86,6 +91,7 @@ fun DashboardAnnouncementBannerSection( state.cardState, mainNavController, homeNavController, + modifier ) } } diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/course/DashboardCourseSection.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/course/DashboardCourseSection.kt index b415e77c14..6b13d038fc 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/course/DashboardCourseSection.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/course/DashboardCourseSection.kt @@ -35,6 +35,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.navigation.NavGraph.Companion.findStartDestination @@ -70,7 +72,8 @@ fun DashboardCourseSection( mainNavController: NavHostController, homeNavController: NavHostController, shouldRefresh: Boolean, - refreshState: MutableStateFlow> + refreshState: MutableStateFlow>, + modifier: Modifier = Modifier, ) { val viewModel = hiltViewModel() val state by viewModel.uiState.collectAsState() @@ -84,14 +87,15 @@ fun DashboardCourseSection( } } - DashboardCourseSection(state, mainNavController, homeNavController) + DashboardCourseSection(state, mainNavController, homeNavController, modifier) } @Composable fun DashboardCourseSection( state: DashboardCourseUiState, mainNavController: NavHostController, - homeNavController: NavHostController + homeNavController: NavHostController, + modifier: Modifier = Modifier, ) { when(state.state) { DashboardItemState.LOADING -> { @@ -99,14 +103,14 @@ fun DashboardCourseSection( DashboardCourseCardState.Loading, { handleClickAction(it, mainNavController, homeNavController) }, true, - modifier = Modifier.padding(horizontal = 24.dp) + modifier = modifier.padding(horizontal = 24.dp) ) } DashboardItemState.ERROR -> { - DashboardCourseCardError({state.onRefresh {} }, Modifier.padding(horizontal = 24.dp)) + DashboardCourseCardError({state.onRefresh {} }, modifier.padding(horizontal = 24.dp)) } DashboardItemState.SUCCESS -> { - DashboardCourseSectionContent(state, mainNavController, homeNavController) + DashboardCourseSectionContent(state, mainNavController, homeNavController, modifier) } } } @@ -115,13 +119,15 @@ fun DashboardCourseSection( private fun DashboardCourseSectionContent( state: DashboardCourseUiState, mainNavController: NavHostController, - homeNavController: NavHostController + homeNavController: NavHostController, + modifier: Modifier = Modifier, ) { // Display 4 cards at most val pagerState = rememberPagerState { min(4, state.courses.size) } Column( horizontalAlignment = Alignment.CenterHorizontally, + modifier = modifier ) { if (state.programs.items.isNotEmpty()) { DashboardPaginatedWidgetCard( @@ -151,6 +157,9 @@ private fun DashboardCourseSectionContent( val cardHeight = coordinates.size.height if (cardHeight > maxCardHeight) { maxCardHeight = cardHeight } } + .semantics { + contentDescription = "" + } ) } else -> { @@ -164,7 +173,6 @@ private fun DashboardCourseSectionContent( } } } - } Button( diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/ModuleItemSequenceScreen.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/ModuleItemSequenceScreen.kt index dcc96a3c30..84b0c3a018 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/ModuleItemSequenceScreen.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/ModuleItemSequenceScreen.kt @@ -68,6 +68,8 @@ import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -326,7 +328,12 @@ private fun ModuleHeaderContainer( ) { Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) { Row { - IconButton(iconRes = R.drawable.arrow_back, color = IconButtonColor.Institution, onClick = onBackPressed) + IconButton( + iconRes = R.drawable.arrow_back, + contentDescription = stringResource(R.string.a11yNavigateBack), + color = IconButtonColor.Institution, + onClick = onBackPressed + ) Column( modifier = Modifier .weight(1f) @@ -350,6 +357,7 @@ private fun ModuleHeaderContainer( } IconButton( iconRes = R.drawable.list_alt, + contentDescription = stringResource(R.string.myProgress), color = IconButtonColor.Institution, onClick = uiState.onProgressClick ) @@ -365,7 +373,16 @@ private fun ModuleHeaderContainer( if (index < uiState.currentItem?.detailTags?.lastIndex.orDefault()) listOf(item, "|") else listOf(item) } separatedFlowRowItems.forEach { - Text(text = it, style = HorizonTypography.p2, color = HorizonColors.Text.surfaceColored()) + Text( + text = it, + style = HorizonTypography.p2, + color = HorizonColors.Text.surfaceColored(), + modifier = Modifier.semantics { + if (it == "|") { + hideFromAccessibility() + } + } + ) } } } diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/content/assignment/attempts/AttemptSelectorBottomSheet.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/content/assignment/attempts/AttemptSelectorBottomSheet.kt index 27e4fa21ef..5d1b75c025 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/content/assignment/attempts/AttemptSelectorBottomSheet.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/content/assignment/attempts/AttemptSelectorBottomSheet.kt @@ -74,6 +74,7 @@ fun AttemptSelectorBottomSheet( Box(modifier = Modifier.padding(start = 24.dp, end = 24.dp, top = 24.dp)) { IconButton( iconRes = R.drawable.close, + contentDescription = stringResource(R.string.a11y_close), color = IconButtonColor.Inverse, modifier = Modifier .align(Alignment.CenterEnd) diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/content/assignment/comments/CommentsDialog.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/content/assignment/comments/CommentsDialog.kt index debb61ca36..b22d6fc0ff 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/content/assignment/comments/CommentsDialog.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/content/assignment/comments/CommentsDialog.kt @@ -103,6 +103,7 @@ fun CommentsDialog( Box(modifier = Modifier.padding(start = 24.dp, end = 24.dp, top = 24.dp)) { IconButton( iconRes = R.drawable.close, + contentDescription = stringResource(R.string.a11y_close), color = IconButtonColor.Inverse, modifier = Modifier .align(Alignment.CenterEnd) diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/progress/ProgressScreen.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/progress/ProgressScreen.kt index d723998181..ec222fd1c8 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/progress/ProgressScreen.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/moduleitemsequence/progress/ProgressScreen.kt @@ -24,11 +24,14 @@ import androidx.compose.animation.slideInHorizontally import androidx.compose.animation.slideOutHorizontally import androidx.compose.animation.togetherWith import androidx.compose.foundation.background +import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -39,8 +42,13 @@ import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -64,6 +72,7 @@ import com.instructure.horizon.horizonui.platform.LoadingState import com.instructure.horizon.model.LearningObjectStatus import com.instructure.horizon.model.LearningObjectType import com.instructure.pandautils.compose.modifiers.conditional +import kotlinx.coroutines.delay @Composable fun ProgressScreen(uiState: ProgressScreenUiState, loadingState: LoadingState, modifier: Modifier = Modifier) { @@ -74,32 +83,57 @@ fun ProgressScreen(uiState: ProgressScreenUiState, loadingState: LoadingState, m dragHandle = null, modifier = Modifier.padding(top = 48.dp) ) { - Box( + Column( modifier = modifier .fillMaxSize() .background(color = HorizonColors.Surface.pagePrimary(), shape = HorizonCornerRadius.level5) .padding(start = 24.dp, end = 24.dp) ) { - when { - loadingState.isLoading -> Spinner(modifier = Modifier.align(Alignment.Center)) - - loadingState.isError && uiState.pages.isEmpty() -> Text( - text = stringResource(R.string.moduleProgressScreen_error), - style = HorizonTypography.h3 + Box { + Row( + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxWidth() + .padding(top = 30.dp) + .align(Alignment.Center), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + painterResource(R.drawable.list_alt), + contentDescription = null, + tint = HorizonColors.Icon.default() + ) + HorizonSpace(SpaceSize.SPACE_8) + Text( + text = stringResource(R.string.moduleProgressScreen_title), + style = HorizonTypography.h3, + modifier = Modifier + ) + } + IconButton( + iconRes = R.drawable.close, + contentDescription = stringResource(R.string.close), + color = IconButtonColor.Inverse, + modifier = Modifier + .align(Alignment.TopEnd) + .padding(top = 30.dp), + elevation = HorizonElevation.level4, + onClick = uiState.onCloseClick, + size = IconButtonSize.SMALL ) + } + Box(modifier = Modifier.fillMaxHeight()) { + when { + loadingState.isLoading -> Spinner(modifier = Modifier.align(Alignment.Center)) + + loadingState.isError && uiState.pages.isEmpty() -> Text( + text = stringResource(R.string.moduleProgressScreen_error), + style = HorizonTypography.h3 + ) - else -> ProgressScreenContent(uiState) + else -> ProgressScreenContent(uiState) + } } - IconButton( - iconRes = R.drawable.close, - color = IconButtonColor.Inverse, - modifier = Modifier - .align(Alignment.TopEnd) - .padding(top = 30.dp), - elevation = HorizonElevation.level4, - onClick = uiState.onCloseClick, - size = IconButtonSize.SMALL - ) } } } @@ -107,6 +141,8 @@ fun ProgressScreen(uiState: ProgressScreenUiState, loadingState: LoadingState, m @Composable private fun BoxScope.ProgressScreenContent(uiState: ProgressScreenUiState) { val currentPage = uiState.pages[uiState.currentPosition] + val hasPageChanged = remember { mutableStateOf(false) } + AnimatedContent( currentPage, transitionSpec = { EnterTransition.None togetherWith ExitTransition.None }, label = "lazyListContent", @@ -121,31 +157,19 @@ private fun BoxScope.ProgressScreenContent(uiState: ProgressScreenUiState) { ) ) } + val focusRequester = remember { FocusRequester() } + LaunchedEffect(uiState.currentPosition) { + if (hasPageChanged.value) { + delay(300) + focusRequester.requestFocus() + } else { + hasPageChanged.value = true + } + } LazyColumn( verticalArrangement = Arrangement.spacedBy(8.dp), contentPadding = PaddingValues(top = 24.dp, bottom = 90.dp) ) { - item { - Row( - horizontalArrangement = Arrangement.Center, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp) - .align(Alignment.Center), - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - painterResource(R.drawable.list_alt), - contentDescription = null, - tint = HorizonColors.Icon.default() - ) - HorizonSpace(SpaceSize.SPACE_8) - Text( - text = stringResource(R.string.moduleProgressScreen_title), - style = HorizonTypography.h3 - ) - } - } item { Row( verticalAlignment = Alignment.CenterVertically, @@ -156,7 +180,10 @@ private fun BoxScope.ProgressScreenContent(uiState: ProgressScreenUiState) { Text( text = currentPageTarget.moduleName, style = HorizonTypography.labelLargeBold, - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier + .padding(top = 16.dp) + .focusRequester(focusRequester) + .focusable() ) } } @@ -186,6 +213,7 @@ private fun BoxScope.ProgressScreenContent(uiState: ProgressScreenUiState) { } if (uiState.currentPosition > 0) IconButton( iconRes = R.drawable.chevron_left, + contentDescription = stringResource(R.string.a11y_previousModule), color = IconButtonColor.Inverse, modifier = Modifier .align(Alignment.BottomStart) @@ -195,6 +223,7 @@ private fun BoxScope.ProgressScreenContent(uiState: ProgressScreenUiState) { ) if (uiState.currentPosition < uiState.pages.size - 1) IconButton( iconRes = R.drawable.chevron_right, + contentDescription = stringResource(R.string.a11y_nextModule), color = IconButtonColor.Inverse, modifier = Modifier .align(Alignment.BottomEnd) diff --git a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/Extensions.kt b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/Extensions.kt index 2c3c47e158..036fd5f3ed 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/Extensions.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/Extensions.kt @@ -41,7 +41,19 @@ fun SemanticsPropertyReceiver.selectable(context: Context, selected: Boolean) { val unselectedStateDesc = context.getString(R.string.a11y_unselected) stateDescription = if (selected) selectedStateDesc else unselectedStateDesc + if (selected) { + liveRegion = LiveRegionMode.Assertive + } +} + +fun SemanticsPropertyReceiver.toggleable(context: Context, toggledOn: Boolean) { + val onStateDesc = context.getString(R.string.a11y_on) + val offStateDesc = context.getString(R.string.a11y_off) + val toggleActionLabel = context.getString(R.string.a11y_toggle) + + stateDescription = if (toggledOn) onStateDesc else offStateDesc liveRegion = LiveRegionMode.Assertive + onClick(toggleActionLabel) { false } } val BoxWithConstraintsScope.isWideLayout diff --git a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/molecules/DropdownChip.kt b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/molecules/DropdownChip.kt index 4b75991219..7441d22c16 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/molecules/DropdownChip.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/molecules/DropdownChip.kt @@ -39,6 +39,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -46,6 +47,7 @@ import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -57,6 +59,7 @@ import com.instructure.horizon.horizonui.foundation.HorizonColors import com.instructure.horizon.horizonui.foundation.HorizonCornerRadius import com.instructure.horizon.horizonui.foundation.HorizonTypography import com.instructure.horizon.horizonui.organisms.inputs.common.InputDropDownPopup +import com.instructure.horizon.horizonui.selectable import com.instructure.pandautils.compose.modifiers.conditional data class DropdownItem( @@ -186,6 +189,7 @@ private fun DropdownChipItem( selectedItem: DropdownItem? ) { val isSelected = selectedItem?.value == item.value + val context = LocalContext.current Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier @@ -196,6 +200,9 @@ private fun DropdownChipItem( ) } .padding(horizontal = 12.dp, vertical = 10.dp) + .semantics { + selectable(context, isSelected) + } ) { item.iconRes?.let { iconRes -> Icon( diff --git a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/molecules/SegmentedControl.kt b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/molecules/SegmentedControl.kt index c129174f9f..c63baab7df 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/molecules/SegmentedControl.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/molecules/SegmentedControl.kt @@ -42,6 +42,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp @@ -53,6 +54,7 @@ import com.instructure.horizon.horizonui.foundation.HorizonCornerRadius import com.instructure.horizon.horizonui.foundation.HorizonSpace import com.instructure.horizon.horizonui.foundation.HorizonTypography import com.instructure.horizon.horizonui.foundation.SpaceSize +import com.instructure.horizon.horizonui.selectable import com.instructure.pandautils.utils.toDp /** @@ -84,6 +86,7 @@ fun SegmentedControl( ) Box(modifier = modifier.fillMaxWidth()) { + val context = LocalContext.current Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, modifier = modifier @@ -111,6 +114,9 @@ fun SegmentedControl( .onGloballyPositioned { itemWidth.intValue = it.size.width } + .semantics { + selectable(context, selectedIndex == index) + } ) { Row(verticalAlignment = Alignment.CenterVertically) { val contentColor = diff --git a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/cards/AttemptCard.kt b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/cards/AttemptCard.kt index a820a4d127..a043242f56 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/cards/AttemptCard.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/cards/AttemptCard.kt @@ -26,6 +26,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.instructure.canvasapi2.utils.ContextKeeper @@ -39,6 +40,7 @@ import com.instructure.horizon.horizonui.molecules.Pill import com.instructure.horizon.horizonui.molecules.PillCase import com.instructure.horizon.horizonui.molecules.PillSize import com.instructure.horizon.horizonui.molecules.PillType +import com.instructure.horizon.horizonui.selectable import com.instructure.pandautils.compose.modifiers.conditional data class AttemptCardState( @@ -54,6 +56,7 @@ data class AttemptCardState( @Composable fun AttemptCard(state: AttemptCardState, modifier: Modifier = Modifier) { val onClick = state.onClick + val context = LocalContext.current Card( shape = HorizonCornerRadius.level2, colors = CardDefaults.cardColors().copy(containerColor = HorizonColors.Surface.cardPrimary()), @@ -63,6 +66,11 @@ fun AttemptCard(state: AttemptCardState, modifier: Modifier = Modifier) { .conditional(onClick != null) { clickable { onClick?.invoke() } } + .semantics { + if (state.selected) { + selectable(context, true) + } + } ) { Column( modifier = Modifier.padding(vertical = 16.dp, horizontal = 16.dp) diff --git a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/cards/CollapsableContentCard.kt b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/cards/CollapsableContentCard.kt index 5db2a4128f..11291ed62b 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/cards/CollapsableContentCard.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/cards/CollapsableContentCard.kt @@ -38,7 +38,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource -import androidx.compose.ui.semantics.invisibleToUser import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -70,11 +69,12 @@ fun CollapsableContentCard( modifier = Modifier .padding(vertical = 16.dp) ) { + val context = LocalContext.current Column( modifier = Modifier .clickable { onExpandChanged(!expanded) } - .semantics { - invisibleToUser() + .semantics(mergeDescendants = true) { + expandable(context, expanded) } ){ Text( @@ -91,14 +91,10 @@ fun CollapsableContentCard( targetValue = if (expanded) 180f else 0f, label = "rotationAnimation" ) - val context = LocalContext.current Row( modifier = Modifier .fillMaxWidth() .padding(horizontal = 24.dp) - .semantics(mergeDescendants = true) { - expandable(context, expanded) - } ) { Icon( diff --git a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/cards/ModuleItemCard.kt b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/cards/ModuleItemCard.kt index 5bdb5e5e96..0f0af0662a 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/cards/ModuleItemCard.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/cards/ModuleItemCard.kt @@ -35,6 +35,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.instructure.canvasapi2.utils.ContextKeeper @@ -45,6 +46,7 @@ import com.instructure.horizon.horizonui.foundation.HorizonCornerRadius import com.instructure.horizon.horizonui.foundation.HorizonSpace import com.instructure.horizon.horizonui.foundation.HorizonTypography import com.instructure.horizon.horizonui.foundation.SpaceSize +import com.instructure.horizon.horizonui.selectable import com.instructure.horizon.model.LearningObjectStatus import com.instructure.horizon.model.LearningObjectType import com.instructure.pandautils.utils.localisedFormatMonthDay @@ -66,6 +68,7 @@ data class ModuleItemCardState( @Composable fun ModuleItemCard(state: ModuleItemCardState, modifier: Modifier = Modifier) { val onClick = state.onClick + val context = LocalContext.current Card( shape = HorizonCornerRadius.level2, colors = CardDefaults.cardColors().copy(containerColor = HorizonColors.Surface.cardPrimary()), @@ -73,7 +76,14 @@ fun ModuleItemCard(state: ModuleItemCardState, modifier: Modifier = Modifier) { modifier = modifier ) { val clickModifier = if (onClick != null) Modifier.clickable { onClick() } else Modifier - Row(verticalAlignment = Alignment.CenterVertically, modifier = clickModifier) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = clickModifier.semantics { + if (state.selected) { + selectable(context, true) + } + } + ) { Column( modifier = Modifier .padding(vertical = 12.dp, horizontal = 16.dp) diff --git a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/controls/SwitchItem.kt b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/controls/SwitchItem.kt index b3a97ee439..cc72183112 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/controls/SwitchItem.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/controls/SwitchItem.kt @@ -40,7 +40,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.Role.Companion.Switch +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp @@ -48,6 +52,7 @@ import com.instructure.horizon.R import com.instructure.horizon.horizonui.foundation.HorizonColors import com.instructure.horizon.horizonui.foundation.HorizonSpace import com.instructure.horizon.horizonui.foundation.SpaceSize +import com.instructure.horizon.horizonui.toggleable import com.instructure.pandautils.utils.toPx private const val ANIMATION_DURATION = 250 @@ -62,7 +67,15 @@ data class SwitchItemState( @Composable fun SwitchItem(state: SwitchItemState, modifier: Modifier = Modifier) { val alphaModifier = if (state.enabled) modifier else modifier.alpha(0.5f) - Row(modifier = alphaModifier) { + val context = LocalContext.current + Row(modifier = alphaModifier + .semantics(mergeDescendants = true) { + role = Switch + toggleable( + context = context, + toggledOn = state.checked + ) + }) { HorizonSwitch( checked = state.checked, onCheckedChange = state.onCheckedChanged, diff --git a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/inputs/singleselect/SingleSelect.kt b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/inputs/singleselect/SingleSelect.kt index 82be340a4c..5e306de086 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/inputs/singleselect/SingleSelect.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/horizonui/organisms/inputs/singleselect/SingleSelect.kt @@ -43,6 +43,7 @@ import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -54,6 +55,7 @@ import com.instructure.horizon.horizonui.foundation.HorizonTypography import com.instructure.horizon.horizonui.organisms.inputs.common.Input import com.instructure.horizon.horizonui.organisms.inputs.common.InputContainer import com.instructure.horizon.horizonui.organisms.inputs.common.InputDropDownPopup +import com.instructure.horizon.horizonui.selectable import com.instructure.pandautils.compose.modifiers.conditional @Composable @@ -122,6 +124,7 @@ fun SingleSelect( @Composable private fun SingleSelectItem(option: T, state: SingleSelectState) { + val context = LocalContext.current Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier @@ -129,6 +132,9 @@ private fun SingleSelectItem(option: T, state: SingleSelectState) { .conditional(option == state.selectedOption && state.isMenuOpen) { background(HorizonColors.Surface.institution()) } + .semantics { + selectable(context, option == state.selectedOption) + } ) { Text( text = option.toString(), diff --git a/libs/horizon/src/main/res/values/strings.xml b/libs/horizon/src/main/res/values/strings.xml index e205605507..9ec78daec2 100644 --- a/libs/horizon/src/main/res/values/strings.xml +++ b/libs/horizon/src/main/res/values/strings.xml @@ -408,6 +408,9 @@ Not completed Locked Unselected + On + Off + Toggle From %1$s Go to announcement We weren\'t able to load this content.\nPlease try again. @@ -486,9 +489,11 @@ Read Unread Previous module item + Previous module Open AI Assistant Open notebook Open more options Next module item + Next module %1$s, marked as %2$s, double tap to edit note \ No newline at end of file