diff --git a/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/course/HorizonDashboardCourseSectionUiTest.kt b/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/course/HorizonDashboardCourseSectionUiTest.kt index e48e6049f2..699545f139 100644 --- a/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/course/HorizonDashboardCourseSectionUiTest.kt +++ b/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/course/HorizonDashboardCourseSectionUiTest.kt @@ -22,8 +22,10 @@ import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performScrollTo import androidx.navigation.compose.rememberNavController import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.instructure.horizon.R import com.instructure.horizon.features.dashboard.DashboardItemState import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardButtonRoute +import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardHeaderState import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardItemState import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardState import com.instructure.horizon.features.dashboard.widget.course.DashboardCourseSection @@ -32,6 +34,7 @@ import com.instructure.horizon.features.dashboard.widget.course.card.CardClickAc import com.instructure.horizon.features.dashboard.widget.course.card.DashboardCourseCardModuleItemState import com.instructure.horizon.features.dashboard.widget.course.card.DashboardCourseCardParentProgramState import com.instructure.horizon.features.dashboard.widget.course.card.DashboardCourseCardState +import com.instructure.horizon.horizonui.foundation.HorizonColors import com.instructure.horizon.model.LearningObjectType import org.junit.Rule import org.junit.Test @@ -50,6 +53,11 @@ class HorizonDashboardCourseSectionUiTest { programs = DashboardPaginatedWidgetCardState( listOf( DashboardPaginatedWidgetCardItemState( + headerState = DashboardPaginatedWidgetCardHeaderState( + label = "Program", + color = HorizonColors.Surface.institution().copy(0.1f), + iconRes = R.drawable.book_2 + ), title = "Program 1", route = DashboardPaginatedWidgetCardButtonRoute.HomeRoute("") ) diff --git a/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/announcement/DashboardAnnouncementBannerWidgetUiTest.kt b/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/announcement/DashboardAnnouncementBannerWidgetUiTest.kt index d700edea7e..06ed0eca64 100644 --- a/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/announcement/DashboardAnnouncementBannerWidgetUiTest.kt +++ b/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/announcement/DashboardAnnouncementBannerWidgetUiTest.kt @@ -16,9 +16,10 @@ */ package com.instructure.horizon.ui.features.dashboard.widget.announcement -import androidx.compose.ui.test.assertHasClickAction import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onFirst import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.navigation.compose.rememberNavController @@ -27,12 +28,12 @@ import androidx.test.platform.app.InstrumentationRegistry import com.instructure.horizon.R import com.instructure.horizon.features.dashboard.DashboardItemState import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardButtonRoute -import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardChipState +import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardHeaderState import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardItemState import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardState import com.instructure.horizon.features.dashboard.widget.announcement.DashboardAnnouncementBannerSection import com.instructure.horizon.features.dashboard.widget.announcement.DashboardAnnouncementBannerUiState -import com.instructure.horizon.horizonui.molecules.StatusChipColor +import com.instructure.horizon.horizonui.foundation.HorizonColors import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -77,9 +78,8 @@ class DashboardAnnouncementBannerWidgetUiTest { val errorMessage = context.getString(R.string.dashboardAnnouncementBannerErrorMessage) composeTestRule.onNodeWithText(errorMessage, substring = true).assertIsDisplayed() - composeTestRule.onNodeWithText("Refresh") + composeTestRule.onNodeWithText("Refresh", useUnmergedTree = true) .assertIsDisplayed() - .assertHasClickAction() .performClick() assert(refreshCalled) { "Refresh callback should be called when retry button is clicked" } @@ -88,9 +88,10 @@ class DashboardAnnouncementBannerWidgetUiTest { @Test fun testSuccessStateWithSingleAnnouncementDisplaysCorrectly() { val testAnnouncement = DashboardPaginatedWidgetCardItemState( - chipState = DashboardPaginatedWidgetCardChipState( + headerState = DashboardPaginatedWidgetCardHeaderState( label = "Announcement", - color = StatusChipColor.Sky + color = HorizonColors.Surface.institution().copy(alpha = 0.1f), + iconRes = R.drawable.campaign ), title = "Important Announcement", source = "Course Name", @@ -122,10 +123,10 @@ class DashboardAnnouncementBannerWidgetUiTest { fun testSuccessStateWithMultipleAnnouncementsDisplaysAllItems() { val announcements = listOf( DashboardPaginatedWidgetCardItemState( - pageState = "1 of 2", - chipState = DashboardPaginatedWidgetCardChipState( + headerState = DashboardPaginatedWidgetCardHeaderState( label = "Announcement", - color = StatusChipColor.Sky + color = HorizonColors.Surface.institution().copy(alpha = 0.1f), + iconRes = R.drawable.campaign ), title = "First Announcement", source = "Course 1", @@ -133,10 +134,10 @@ class DashboardAnnouncementBannerWidgetUiTest { route = DashboardPaginatedWidgetCardButtonRoute.MainRoute("") ), DashboardPaginatedWidgetCardItemState( - pageState = "2 of 2", - chipState = DashboardPaginatedWidgetCardChipState( + headerState = DashboardPaginatedWidgetCardHeaderState( label = "Announcement", - color = StatusChipColor.Sky + color = HorizonColors.Surface.institution().copy(alpha = 0.1f), + iconRes = R.drawable.campaign ), title = "Second Announcement", source = "Course 2", @@ -156,19 +157,18 @@ class DashboardAnnouncementBannerWidgetUiTest { } composeTestRule.onNodeWithText("First Announcement").assertIsDisplayed() - composeTestRule.onNodeWithText( - context.getString(R.string.dashboardAnnouncementBannerFrom, "Course 1") - ).assertIsDisplayed() + composeTestRule.onNodeWithText(context.getString(R.string.dashboardAnnouncementBannerFrom, "Course 1")).assertIsDisplayed() - composeTestRule.onNodeWithText("1 of 2", useUnmergedTree = true).assertIsDisplayed() + composeTestRule.onAllNodesWithText("1 of 2").onFirst().assertIsDisplayed() } @Test fun testSuccessStateWithGlobalAnnouncementWithoutSource() { val testAnnouncement = DashboardPaginatedWidgetCardItemState( - chipState = DashboardPaginatedWidgetCardChipState( + headerState = DashboardPaginatedWidgetCardHeaderState( label = "Announcement", - color = StatusChipColor.Sky + color = HorizonColors.Surface.institution().copy(alpha = 0.1f), + iconRes = R.drawable.campaign ), title = "Global Announcement", source = null, @@ -195,9 +195,10 @@ class DashboardAnnouncementBannerWidgetUiTest { @Test fun testSuccessStateAnnouncementIsClickable() { val testAnnouncement = DashboardPaginatedWidgetCardItemState( - chipState = DashboardPaginatedWidgetCardChipState( + headerState = DashboardPaginatedWidgetCardHeaderState( label = "Announcement", - color = StatusChipColor.Sky + color = HorizonColors.Surface.institution().copy(alpha = 0.1f), + iconRes = R.drawable.campaign ), title = "Test Announcement", source = "Test Course", @@ -224,9 +225,10 @@ class DashboardAnnouncementBannerWidgetUiTest { fun testSuccessStateDisplaysDateCorrectly() { val testDate = Date(1704067200000L) val testAnnouncement = DashboardPaginatedWidgetCardItemState( - chipState = DashboardPaginatedWidgetCardChipState( + headerState = DashboardPaginatedWidgetCardHeaderState( label = "Announcement", - color = StatusChipColor.Sky + color = HorizonColors.Surface.institution().copy(alpha = 0.1f), + iconRes = R.drawable.campaign ), title = "Dated Announcement", source = "Test Course", @@ -250,9 +252,10 @@ class DashboardAnnouncementBannerWidgetUiTest { @Test fun testSuccessStateWithAnnouncementWithoutDate() { val testAnnouncement = DashboardPaginatedWidgetCardItemState( - chipState = DashboardPaginatedWidgetCardChipState( + headerState = DashboardPaginatedWidgetCardHeaderState( label = "Announcement", - color = StatusChipColor.Sky + color = HorizonColors.Surface.institution().copy(alpha = 0.1f), + iconRes = R.drawable.campaign ), title = "No Date Announcement", source = "Test Course", @@ -277,9 +280,10 @@ class DashboardAnnouncementBannerWidgetUiTest { fun testSuccessStateWithLongAnnouncementTitle() { val longTitle = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. This is a very long announcement title that should be displayed properly in the widget." val testAnnouncement = DashboardPaginatedWidgetCardItemState( - chipState = DashboardPaginatedWidgetCardChipState( + headerState = DashboardPaginatedWidgetCardHeaderState( label = "Announcement", - color = StatusChipColor.Sky + color = HorizonColors.Surface.institution().copy(alpha = 0.1f), + iconRes = R.drawable.campaign ), title = longTitle, source = "Test Course", diff --git a/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/myprogress/DashboardMyProgressWidgetUiTest.kt b/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/myprogress/DashboardMyProgressWidgetUiTest.kt index db34581605..915dcc5e51 100644 --- a/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/myprogress/DashboardMyProgressWidgetUiTest.kt +++ b/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/myprogress/DashboardMyProgressWidgetUiTest.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 import com.instructure.horizon.features.dashboard.DashboardItemState +import com.instructure.horizon.features.dashboard.widget.DashboardWidgetPageState import com.instructure.horizon.features.dashboard.widget.myprogress.DashboardMyProgressSection import com.instructure.horizon.features.dashboard.widget.myprogress.DashboardMyProgressUiState import com.instructure.horizon.features.dashboard.widget.myprogress.card.DashboardMyProgressCardState @@ -42,7 +43,7 @@ class DashboardMyProgressWidgetUiTest { ) composeTestRule.setContent { - DashboardMyProgressSection(state) + DashboardMyProgressSection(state, DashboardWidgetPageState.Empty) } composeTestRule.waitForIdle() @@ -60,7 +61,7 @@ class DashboardMyProgressWidgetUiTest { ) composeTestRule.setContent { - DashboardMyProgressSection(state) + DashboardMyProgressSection(state, DashboardWidgetPageState.Empty) } composeTestRule.onNodeWithText("Activities").assertIsDisplayed() @@ -85,7 +86,7 @@ class DashboardMyProgressWidgetUiTest { ) composeTestRule.setContent { - DashboardMyProgressSection(state) + DashboardMyProgressSection(state, DashboardWidgetPageState.Empty) } composeTestRule.onNodeWithText("Activities").assertIsDisplayed() @@ -105,7 +106,7 @@ class DashboardMyProgressWidgetUiTest { ) composeTestRule.setContent { - DashboardMyProgressSection(state) + DashboardMyProgressSection(state, DashboardWidgetPageState.Empty) } composeTestRule.onNodeWithText("This widget will update once data becomes available.") diff --git a/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/skilloverview/DashboardSkillOverviewWidgetUiTest.kt b/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/skilloverview/DashboardSkillOverviewWidgetUiTest.kt index dbc99d6a41..d26a5918ec 100644 --- a/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/skilloverview/DashboardSkillOverviewWidgetUiTest.kt +++ b/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/skilloverview/DashboardSkillOverviewWidgetUiTest.kt @@ -9,6 +9,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.instructure.horizon.R import com.instructure.horizon.features.dashboard.DashboardItemState +import com.instructure.horizon.features.dashboard.widget.DashboardWidgetPageState import com.instructure.horizon.features.dashboard.widget.skilloverview.DashboardSkillOverviewSection import com.instructure.horizon.features.dashboard.widget.skilloverview.DashboardSkillOverviewUiState import com.instructure.horizon.features.dashboard.widget.skilloverview.card.DashboardSkillOverviewCardState @@ -30,7 +31,7 @@ class DashboardSkillOverviewWidgetUiTest { ) composeTestRule.setContent { - DashboardSkillOverviewSection(uiState, rememberNavController()) + DashboardSkillOverviewSection(uiState, DashboardWidgetPageState.Empty, rememberNavController()) } val title = context.getString(R.string.dashboardSkillOverviewTitle) @@ -45,7 +46,7 @@ class DashboardSkillOverviewWidgetUiTest { ) composeTestRule.setContent { - DashboardSkillOverviewSection(uiState, rememberNavController()) + DashboardSkillOverviewSection(uiState, DashboardWidgetPageState.Empty, rememberNavController()) } val title = context.getString(R.string.dashboardSkillOverviewTitle) @@ -66,7 +67,7 @@ class DashboardSkillOverviewWidgetUiTest { ) composeTestRule.setContent { - DashboardSkillOverviewSection(uiState, rememberNavController()) + DashboardSkillOverviewSection(uiState, DashboardWidgetPageState.Empty, rememberNavController()) } val retryLabel = context.getString(R.string.dashboardWidgetCardErrorRetry) @@ -83,7 +84,7 @@ class DashboardSkillOverviewWidgetUiTest { ) composeTestRule.setContent { - DashboardSkillOverviewSection(uiState, rememberNavController()) + DashboardSkillOverviewSection(uiState, DashboardWidgetPageState.Empty, rememberNavController()) } val title = context.getString(R.string.dashboardSkillOverviewTitle) @@ -101,7 +102,7 @@ class DashboardSkillOverviewWidgetUiTest { ) composeTestRule.setContent { - DashboardSkillOverviewSection(uiState, rememberNavController()) + DashboardSkillOverviewSection(uiState, DashboardWidgetPageState.Empty, rememberNavController()) } val title = context.getString(R.string.dashboardSkillOverviewTitle) @@ -120,7 +121,7 @@ class DashboardSkillOverviewWidgetUiTest { ) composeTestRule.setContent { - DashboardSkillOverviewSection(uiState, rememberNavController()) + DashboardSkillOverviewSection(uiState, DashboardWidgetPageState.Empty, rememberNavController()) } val title = context.getString(R.string.dashboardSkillOverviewTitle) @@ -139,7 +140,7 @@ class DashboardSkillOverviewWidgetUiTest { ) composeTestRule.setContent { - DashboardSkillOverviewSection(uiState, rememberNavController()) + DashboardSkillOverviewSection(uiState, DashboardWidgetPageState.Empty, rememberNavController()) } val title = context.getString(R.string.dashboardSkillOverviewTitle) @@ -158,7 +159,7 @@ class DashboardSkillOverviewWidgetUiTest { ) composeTestRule.setContent { - DashboardSkillOverviewSection(uiState, rememberNavController()) + DashboardSkillOverviewSection(uiState, DashboardWidgetPageState.Empty, rememberNavController()) } val title = context.getString(R.string.dashboardSkillOverviewTitle) diff --git a/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/timespent/DashboardTimeSpentWidgetUiTest.kt b/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/timespent/DashboardTimeSpentWidgetUiTest.kt index 8c04bc3888..403b3aaba8 100644 --- a/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/timespent/DashboardTimeSpentWidgetUiTest.kt +++ b/libs/horizon/src/androidTest/java/com/instructure/horizon/ui/features/dashboard/widget/timespent/DashboardTimeSpentWidgetUiTest.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 import com.instructure.horizon.features.dashboard.DashboardItemState +import com.instructure.horizon.features.dashboard.widget.DashboardWidgetPageState import com.instructure.horizon.features.dashboard.widget.timespent.DashboardTimeSpentSection import com.instructure.horizon.features.dashboard.widget.timespent.DashboardTimeSpentUiState import com.instructure.horizon.features.dashboard.widget.timespent.card.CourseOption @@ -43,7 +44,7 @@ class DashboardTimeSpentWidgetUiTest { ) composeTestRule.setContent { - DashboardTimeSpentSection(state) + DashboardTimeSpentSection(state, DashboardWidgetPageState.Empty) } composeTestRule.waitForIdle() @@ -61,7 +62,7 @@ class DashboardTimeSpentWidgetUiTest { ) composeTestRule.setContent { - DashboardTimeSpentSection(state) + DashboardTimeSpentSection(state, DashboardWidgetPageState.Empty) } composeTestRule.onNodeWithText("Time learning").assertIsDisplayed() @@ -90,7 +91,7 @@ class DashboardTimeSpentWidgetUiTest { ) composeTestRule.setContent { - DashboardTimeSpentSection(state) + DashboardTimeSpentSection(state, DashboardWidgetPageState.Empty) } composeTestRule.onNodeWithText("Time learning").assertIsDisplayed() @@ -116,7 +117,7 @@ class DashboardTimeSpentWidgetUiTest { ) composeTestRule.setContent { - DashboardTimeSpentSection(state) + DashboardTimeSpentSection(state, DashboardWidgetPageState.Empty) } composeTestRule.onNodeWithText("Time learning").assertIsDisplayed() @@ -143,7 +144,7 @@ class DashboardTimeSpentWidgetUiTest { ) composeTestRule.setContent { - DashboardTimeSpentSection(state) + DashboardTimeSpentSection(state, DashboardWidgetPageState.Empty) } composeTestRule.onNodeWithText("Time learning").assertIsDisplayed() @@ -172,7 +173,7 @@ class DashboardTimeSpentWidgetUiTest { ) composeTestRule.setContent { - DashboardTimeSpentSection(state) + DashboardTimeSpentSection(state, DashboardWidgetPageState.Empty) } composeTestRule.onNodeWithText("Time learning").assertIsDisplayed() @@ -195,7 +196,7 @@ class DashboardTimeSpentWidgetUiTest { ) composeTestRule.setContent { - DashboardTimeSpentSection(state) + DashboardTimeSpentSection(state, DashboardWidgetPageState.Empty) } composeTestRule.onNodeWithText("Time learning").assertIsDisplayed() @@ -219,7 +220,7 @@ class DashboardTimeSpentWidgetUiTest { ) composeTestRule.setContent { - DashboardTimeSpentSection(state) + DashboardTimeSpentSection(state, DashboardWidgetPageState.Empty) } composeTestRule.onNodeWithText("Time learning").assertIsDisplayed() @@ -237,11 +238,14 @@ class DashboardTimeSpentWidgetUiTest { fun testSuccessStateWithZeroHoursZeroMinutesDisplaysEmpty() { val state = DashboardTimeSpentUiState( state = DashboardItemState.SUCCESS, - cardState = DashboardTimeSpentCardState() + cardState = DashboardTimeSpentCardState( + hours = 0, + minutes = 0, + ) ) composeTestRule.setContent { - DashboardTimeSpentSection(state) + DashboardTimeSpentSection(state, DashboardWidgetPageState.Empty) } // Verify zero hours is not displayed 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 7934525554..b4f1809ca2 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 @@ -71,6 +71,7 @@ import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi import com.bumptech.glide.integration.compose.GlideImage import com.instructure.canvasapi2.utils.ContextKeeper import com.instructure.horizon.R +import com.instructure.horizon.features.dashboard.widget.DashboardWidgetPageState import com.instructure.horizon.features.dashboard.widget.announcement.DashboardAnnouncementBannerWidget import com.instructure.horizon.features.dashboard.widget.course.DashboardCourseSection import com.instructure.horizon.features.dashboard.widget.myprogress.DashboardMyProgressWidget @@ -282,7 +283,8 @@ private fun NumericWidgetRow( homeNavController: NavHostController ) { BoxWithConstraints { - val pagerState = rememberPagerState { 3 } + val pageCount = 3 + val pagerState = rememberPagerState{ pageCount } if (this.isWideLayout) { Row( verticalAlignment = Alignment.CenterVertically, @@ -296,17 +298,20 @@ private fun NumericWidgetRow( DashboardMyProgressWidget( shouldRefresh, refreshStateFlow, + DashboardWidgetPageState.Empty, Modifier.width(IntrinsicSize.Max) ) DashboardTimeSpentWidget( shouldRefresh, refreshStateFlow, + DashboardWidgetPageState.Empty, Modifier.width(IntrinsicSize.Max) ) DashboardSkillOverviewWidget( homeNavController, shouldRefresh, refreshStateFlow, + DashboardWidgetPageState.Empty, Modifier.width(IntrinsicSize.Max) ) } @@ -324,6 +329,7 @@ private fun NumericWidgetRow( DashboardMyProgressWidget( shouldRefresh, refreshStateFlow, + DashboardWidgetPageState(index + 1, pageCount), modifier .fillMaxWidth() .padding(bottom = 16.dp) @@ -334,6 +340,7 @@ private fun NumericWidgetRow( DashboardTimeSpentWidget( shouldRefresh, refreshStateFlow, + DashboardWidgetPageState(index + 1, pageCount), modifier .fillMaxWidth() .padding(bottom = 16.dp) @@ -345,6 +352,7 @@ private fun NumericWidgetRow( homeNavController, shouldRefresh, refreshStateFlow, + DashboardWidgetPageState(index + 1, pageCount), modifier .fillMaxWidth() .padding(bottom = 16.dp) diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardPaginatedWidgetCard.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardPaginatedWidgetCard.kt index 51cc85b63c..49338dbec3 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardPaginatedWidgetCard.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardPaginatedWidgetCard.kt @@ -18,8 +18,6 @@ package com.instructure.horizon.features.dashboard.widget import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues -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.pager.rememberPagerState @@ -29,24 +27,18 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.semantics.role -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 import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController import com.instructure.canvasapi2.utils.ContextKeeper import com.instructure.horizon.R -import com.instructure.horizon.features.dashboard.DashboardCard import com.instructure.horizon.horizonui.animation.shimmerEffect import com.instructure.horizon.horizonui.foundation.HorizonColors 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.molecules.StatusChip -import com.instructure.horizon.horizonui.molecules.StatusChipColor -import com.instructure.horizon.horizonui.molecules.StatusChipState import com.instructure.horizon.horizonui.organisms.AnimatedHorizontalPager import com.instructure.pandautils.utils.localisedFormat import java.util.Date @@ -71,13 +63,23 @@ fun DashboardPaginatedWidgetCard( pageSpacing = 12.dp, verticalAlignment = Alignment.CenterVertically, ) { index, modifier -> - DashboardCard( + val item = state.items[index] + DashboardWidgetCard( + title = item.headerState.label, + iconRes = item.headerState.iconRes, + useMinWidth = false, + isLoading = state.isLoading, + widgetColor = item.headerState.color, + pageState = DashboardWidgetPageState( + currentPageNumber = pagerState.currentPage + 1, + pageCount = state.items.size + ), modifier = modifier.padding(bottom = 16.dp), onClick = if (state.isLoading) { null } else { { - state.items[index].route?.let { route -> + item.route?.let { route -> when (route) { is DashboardPaginatedWidgetCardButtonRoute.HomeRoute -> { homeNavController.navigate(route.route) @@ -96,19 +98,10 @@ fun DashboardPaginatedWidgetCard( modifier = Modifier .fillMaxWidth() ) { - HorizonSpace(SpaceSize.SPACE_24) - DashboardPaginatedWidgetCardItem( - item = state.items[index], + item = item, isLoading = state.isLoading, - modifier = Modifier - .padding(horizontal = 24.dp) - .semantics(mergeDescendants = true) { - role = Role.Button - } ) - - HorizonSpace(SpaceSize.SPACE_24) } } } @@ -124,44 +117,14 @@ private fun DashboardPaginatedWidgetCardItem( Column( modifier = modifier.fillMaxWidth() ) { - if (item.chipState != null || item.pageState != null) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth() - ) { - item.chipState?.let { chipState -> - StatusChip( - state = StatusChipState( - label = chipState.label, - color = chipState.color, - fill = true - ), - modifier = Modifier.shimmerEffect( - isLoading, - backgroundColor = chipState.color.fillColor.copy(0.8f), - shimmerColor = chipState.color.fillColor.copy(0.5f) - ) - ) - } - - Spacer(Modifier.weight(1f)) - - item.pageState?.let { - Text( - it, - style = HorizonTypography.p2, - color = HorizonColors.Text.dataPoint(), - ) - } - } - HorizonSpace(SpaceSize.SPACE_16) - } item.source?.let { source -> Text( text = stringResource( R.string.dashboardAnnouncementBannerFrom, source ), + maxLines = 1, + overflow = TextOverflow.Ellipsis, style = HorizonTypography.p2, color = HorizonColors.Text.dataPoint(), modifier = Modifier.shimmerEffect(isLoading) @@ -182,13 +145,13 @@ private fun DashboardPaginatedWidgetCardItem( Text( text = title, style = HorizonTypography.p1, + maxLines = 3, + overflow = TextOverflow.Ellipsis, color = HorizonColors.Text.body(), modifier = Modifier .fillMaxWidth() .shimmerEffect(isLoading) ) - - HorizonSpace(SpaceSize.SPACE_16) } } } @@ -201,9 +164,10 @@ private fun DashboardPaginatedWidgetCardAnnouncementContentPreview() { state = DashboardPaginatedWidgetCardState( items = listOf( DashboardPaginatedWidgetCardItemState( - chipState = DashboardPaginatedWidgetCardChipState( + headerState = DashboardPaginatedWidgetCardHeaderState( label = "Announcement", - color = StatusChipColor.Sky + color = HorizonColors.Surface.institution().copy(alpha = 0.1f), + iconRes = R.drawable.ic_announcement ), title = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Announcement title shown here.", source = "Institution or Course Name Here", @@ -211,9 +175,10 @@ private fun DashboardPaginatedWidgetCardAnnouncementContentPreview() { route = DashboardPaginatedWidgetCardButtonRoute.MainRoute("") ), DashboardPaginatedWidgetCardItemState( - chipState = DashboardPaginatedWidgetCardChipState( + headerState = DashboardPaginatedWidgetCardHeaderState( label = "Announcement", - color = StatusChipColor.Sky + color = HorizonColors.Surface.institution().copy(alpha = 0.1f), + iconRes = R.drawable.ic_announcement ), title = "Second announcement with different content to show pagination.", source = "Another Course Name", @@ -221,9 +186,10 @@ private fun DashboardPaginatedWidgetCardAnnouncementContentPreview() { route = DashboardPaginatedWidgetCardButtonRoute.MainRoute("") ), DashboardPaginatedWidgetCardItemState( - chipState = DashboardPaginatedWidgetCardChipState( + headerState = DashboardPaginatedWidgetCardHeaderState( label = "Announcement", - color = StatusChipColor.Sky + color = HorizonColors.Surface.institution().copy(alpha = 0.1f), + iconRes = R.drawable.ic_announcement ), title = "Third global announcement without a source.", date = Date(), @@ -241,21 +207,7 @@ private fun DashboardPaginatedWidgetCardAnnouncementContentPreview() { private fun DashboardPaginatedWidgetCardAnnouncementLoadingPreview() { ContextKeeper.appContext = LocalContext.current DashboardPaginatedWidgetCard( - state = DashboardPaginatedWidgetCardState( - items = listOf( - DashboardPaginatedWidgetCardItemState( - chipState = DashboardPaginatedWidgetCardChipState( - label = "Announcement", - color = StatusChipColor.Sky - ), - title = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Announcement title shown here.", - source = "Institution or Course Name Here", - date = Date(), - route = DashboardPaginatedWidgetCardButtonRoute.MainRoute("") - ), - ), - isLoading = true - ), + state = DashboardPaginatedWidgetCardState.Loading, rememberNavController(), rememberNavController(), ) diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardPaginatedWidgetCardState.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardPaginatedWidgetCardState.kt index 31dd37a7a8..b7c251e3a0 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardPaginatedWidgetCardState.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardPaginatedWidgetCardState.kt @@ -1,6 +1,9 @@ package com.instructure.horizon.features.dashboard.widget -import com.instructure.horizon.horizonui.molecules.StatusChipColor +import androidx.annotation.DrawableRes +import androidx.compose.ui.graphics.Color +import com.instructure.horizon.R +import com.instructure.horizon.horizonui.foundation.HorizonColors import java.util.Date data class DashboardPaginatedWidgetCardState( @@ -16,8 +19,7 @@ data class DashboardPaginatedWidgetCardState( } data class DashboardPaginatedWidgetCardItemState( - val chipState: DashboardPaginatedWidgetCardChipState? = null, - val pageState: String? = null, + val headerState: DashboardPaginatedWidgetCardHeaderState, val source: String? = null, val date: Date? = null, val title: String? = null, @@ -25,9 +27,10 @@ data class DashboardPaginatedWidgetCardItemState( ) { companion object { val Loading = DashboardPaginatedWidgetCardItemState( - chipState = DashboardPaginatedWidgetCardChipState( + headerState = DashboardPaginatedWidgetCardHeaderState( label = "Announcement", - color = StatusChipColor.Sky + color = HorizonColors.PrimitivesSky.sky12, + iconRes = R.drawable.campaign ), title = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Announcement title shown here.", source = "Institution or Course Name Here", @@ -37,9 +40,10 @@ data class DashboardPaginatedWidgetCardItemState( } } -data class DashboardPaginatedWidgetCardChipState( +data class DashboardPaginatedWidgetCardHeaderState( val label: String, - val color: StatusChipColor, + val color: Color, + @DrawableRes val iconRes: Int, ) sealed class DashboardPaginatedWidgetCardButtonRoute { 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 9895079e92..e0d1767b0c 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 @@ -18,7 +18,6 @@ package com.instructure.horizon.features.dashboard.widget import androidx.annotation.DrawableRes import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope @@ -38,9 +37,10 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext 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 import com.instructure.horizon.R @@ -52,12 +52,22 @@ import com.instructure.horizon.horizonui.foundation.HorizonTypography import com.instructure.horizon.horizonui.foundation.SpaceSize import com.instructure.pandautils.compose.modifiers.conditional +data class DashboardWidgetPageState( + val currentPageNumber: Int, + val pageCount: Int +) { + companion object { + val Empty = DashboardWidgetPageState(0, 0) + } +} + @Composable fun DashboardWidgetCard( title: String, @DrawableRes iconRes: Int, widgetColor: Color, modifier: Modifier = Modifier, + pageState: DashboardWidgetPageState? = null, isLoading: Boolean = false, useMinWidth: Boolean = true, onClick: (() -> Unit)? = null, @@ -66,7 +76,6 @@ fun DashboardWidgetCard( val context = LocalContext.current DashboardCard( modifier - .semantics(mergeDescendants = true) { } .conditional(isLoading) { clearAndSetSemantics { contentDescription = @@ -83,19 +92,12 @@ fun DashboardWidgetCard( } ) { Row( - horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() + .width(IntrinsicSize.Max) + .padding(bottom = 16.dp) ) { - Text( - text = title, - style = HorizonTypography.labelMediumBold, - color = HorizonColors.Text.dataPoint(), - modifier = Modifier - .padding(end = 8.dp) - .shimmerEffect(isLoading) - ) - Box( contentAlignment = Alignment.Center, modifier = Modifier @@ -116,9 +118,32 @@ fun DashboardWidgetCard( .size(16.dp) ) } - } + HorizonSpace(SpaceSize.SPACE_8) + Text( + text = title, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = HorizonTypography.labelMediumBold, + color = HorizonColors.Text.dataPoint(), + modifier = Modifier + .weight(1f) + .shimmerEffect(isLoading) + ) - HorizonSpace(SpaceSize.SPACE_8) + if (pageState != null && pageState.pageCount > 1) { + HorizonSpace(SpaceSize.SPACE_8) + Text( + stringResource( + R.string.dashboardPaginatedWidgetPagerMessage, + pageState.currentPageNumber, + pageState.pageCount + ), + style = HorizonTypography.p2, + color = HorizonColors.Text.dataPoint(), + modifier = Modifier.shimmerEffect(isLoading) + ) + } + } content() } @@ -139,4 +164,21 @@ private fun DashboardTimeSpentCardPreview() { color = HorizonColors.Text.body() ) } +} + +@Composable +@Preview +private fun DashboardTimeSpentCardPaginatedPreview() { + DashboardWidgetCard( + title = "Time", + pageState = DashboardWidgetPageState(1, 2), + iconRes = R.drawable.schedule, + widgetColor = HorizonColors.PrimitivesBlue.blue12() + ) { + Text( + text = "Content", + style = HorizonTypography.h1, + color = HorizonColors.Text.body() + ) + } } \ No newline at end of file diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardWidgetCardError.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardWidgetCardError.kt index 2253a612c7..d24d88cf6b 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardWidgetCardError.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/DashboardWidgetCardError.kt @@ -47,6 +47,7 @@ fun DashboardWidgetCardError( @DrawableRes iconRes: Int, widgetColor: Color, useMinWidth: Boolean, + pageState: DashboardWidgetPageState, onRetryClick: () -> Unit, modifier: Modifier = Modifier ) { @@ -56,6 +57,7 @@ fun DashboardWidgetCardError( iconRes = iconRes, widgetColor = widgetColor, useMinWidth = useMinWidth, + pageState = pageState, modifier = modifier .semantics(mergeDescendants = true) { role = Role.Button diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/announcement/DashboardAnnouncementBannerViewModel.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/announcement/DashboardAnnouncementBannerViewModel.kt index 5a66d060f1..198d5a1de6 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/announcement/DashboardAnnouncementBannerViewModel.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/announcement/DashboardAnnouncementBannerViewModel.kt @@ -26,10 +26,10 @@ import com.instructure.horizon.features.dashboard.DashboardEvent import com.instructure.horizon.features.dashboard.DashboardEventHandler import com.instructure.horizon.features.dashboard.DashboardItemState import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardButtonRoute -import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardChipState +import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardHeaderState import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardItemState import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardState -import com.instructure.horizon.horizonui.molecules.StatusChipColor +import com.instructure.horizon.horizonui.foundation.HorizonColors import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow @@ -79,8 +79,8 @@ class DashboardAnnouncementBannerViewModel @Inject constructor( it.copy( state = DashboardItemState.SUCCESS, cardState = DashboardPaginatedWidgetCardState( - items = announcements.mapIndexed { index, announcement -> - announcement.toPaginatedWidgetCardItemState(context, index, announcements.size) + items = announcements.map { announcement -> + announcement.toPaginatedWidgetCardItemState(context) } ) ) @@ -100,21 +100,13 @@ class DashboardAnnouncementBannerViewModel @Inject constructor( } } -private fun AnnouncementBannerItem.toPaginatedWidgetCardItemState( - context: Context, - itemIndex: Int, - size: Int, -): DashboardPaginatedWidgetCardItemState { +private fun AnnouncementBannerItem.toPaginatedWidgetCardItemState(context: Context): DashboardPaginatedWidgetCardItemState { return DashboardPaginatedWidgetCardItemState( - chipState = DashboardPaginatedWidgetCardChipState( + headerState = DashboardPaginatedWidgetCardHeaderState( label = context.getString(R.string.notificationsAnnouncementCategoryLabel), - color = StatusChipColor.Sky + color = HorizonColors.Surface.institution().copy(alpha = 0.1f), + iconRes = R.drawable.campaign ), - pageState = if (size > 1) { - context.getString(R.string.dsahboardPaginatedWidgetPagerMessage, itemIndex + 1, size) - } else { - null - }, source = source, date = date, title = title, 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 4e4da8f785..e68278f524 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 @@ -16,19 +16,20 @@ */ package com.instructure.horizon.features.dashboard.widget.announcement -import androidx.compose.foundation.layout.padding 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.unit.dp +import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController +import com.instructure.horizon.R import com.instructure.horizon.features.dashboard.DashboardItemState import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCard import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardState -import com.instructure.horizon.features.dashboard.widget.announcement.card.DashboardAnnouncementBannerCardError +import com.instructure.horizon.features.dashboard.widget.DashboardWidgetCardError +import com.instructure.horizon.features.dashboard.widget.DashboardWidgetPageState +import com.instructure.horizon.horizonui.foundation.HorizonColors import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update @@ -71,9 +72,13 @@ fun DashboardAnnouncementBannerSection( ) } DashboardItemState.ERROR -> { - DashboardAnnouncementBannerCardError( + DashboardWidgetCardError( + stringResource(R.string.notificationsAnnouncementCategoryLabel), + R.drawable.campaign, + HorizonColors.Surface.institution().copy(alpha = 0.1f), + false, + DashboardWidgetPageState.Empty, { state.onRefresh {} }, - Modifier.padding(horizontal = 16.dp) ) } DashboardItemState.SUCCESS -> { diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/announcement/card/DashboardAnnouncementBannerCardError.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/announcement/card/DashboardAnnouncementBannerCardError.kt deleted file mode 100644 index 0e0db7701b..0000000000 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/announcement/card/DashboardAnnouncementBannerCardError.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2025 - present Instructure, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -package com.instructure.horizon.features.dashboard.widget.announcement.card - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.instructure.canvasapi2.utils.ContextKeeper -import com.instructure.horizon.R -import com.instructure.horizon.features.dashboard.DashboardCard -import com.instructure.horizon.horizonui.foundation.HorizonColors -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.molecules.Button -import com.instructure.horizon.horizonui.molecules.ButtonColor -import com.instructure.horizon.horizonui.molecules.ButtonHeight -import com.instructure.horizon.horizonui.molecules.ButtonIconPosition -import com.instructure.horizon.horizonui.molecules.StatusChip -import com.instructure.horizon.horizonui.molecules.StatusChipColor -import com.instructure.horizon.horizonui.molecules.StatusChipState - -@Composable -fun DashboardAnnouncementBannerCardError( - onRetry: () -> Unit, - modifier: Modifier = Modifier -) { - DashboardCard( - modifier = modifier - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(24.dp) - ) { - StatusChip( - state = StatusChipState( - label = stringResource(R.string.notificationsAnnouncementCategoryLabel), - color = StatusChipColor.Sky, - fill = true - ), - ) - HorizonSpace(SpaceSize.SPACE_16) - Text( - text = stringResource(R.string.dashboardAnnouncementBannerErrorMessage), - style = HorizonTypography.p2, - color = HorizonColors.Text.timestamp() - ) - HorizonSpace(SpaceSize.SPACE_8) - Button( - label = stringResource(R.string.dashboardAnnouncementRefreshMessage), - iconPosition = ButtonIconPosition.End(R.drawable.restart_alt), - height = ButtonHeight.SMALL, - onClick = onRetry, - color = ButtonColor.BlackOutline - ) - } - } -} - -@Composable -@Preview -private fun DashboardAnnouncementBannerCardErrorPreview() { - ContextKeeper.appContext = LocalContext.current - DashboardAnnouncementBannerCardError(onRetry = {}) -} diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/course/DashboardMapper.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/course/DashboardMapper.kt index f75bd8c07a..7af4fa3d77 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/course/DashboardMapper.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/course/DashboardMapper.kt @@ -22,7 +22,7 @@ import com.instructure.canvasapi2.managers.graphql.horizon.journey.Program import com.instructure.canvasapi2.type.EnrollmentWorkflowState import com.instructure.horizon.R import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardButtonRoute -import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardChipState +import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardHeaderState import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardItemState import com.instructure.horizon.features.dashboard.widget.course.card.CardClickAction import com.instructure.horizon.features.dashboard.widget.course.card.DashboardCourseCardImageState @@ -30,7 +30,7 @@ import com.instructure.horizon.features.dashboard.widget.course.card.DashboardCo import com.instructure.horizon.features.dashboard.widget.course.card.DashboardCourseCardParentProgramState import com.instructure.horizon.features.dashboard.widget.course.card.DashboardCourseCardState import com.instructure.horizon.features.home.HomeNavigationRoute -import com.instructure.horizon.horizonui.molecules.StatusChipColor +import com.instructure.horizon.horizonui.foundation.HorizonColors internal suspend fun List.mapToDashboardCourseCardState( context: Context, @@ -46,17 +46,13 @@ internal suspend fun List.mapToDashboardCourseCardSt } internal fun List.mapToDashboardCourseCardState(context: Context): List { - return this.mapIndexed { itemIndex, program -> + return this.map { program -> DashboardPaginatedWidgetCardItemState( - chipState = DashboardPaginatedWidgetCardChipState( + headerState = DashboardPaginatedWidgetCardHeaderState( label = context.getString(R.string.dashboardCourseCardProgramChipLabel), - color = StatusChipColor.Grey + color = HorizonColors.Surface.institution().copy(0.1f), + iconRes = R.drawable.book_2 ), - pageState = if (size > 1) { - context.getString(R.string.dsahboardPaginatedWidgetPagerMessage, itemIndex + 1, size) - } else { - null - }, title = context.getString( R.string.dashboardCourseCardProgramDetailsMessage, program.name diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/myprogress/DashboardMyProgressWidget.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/myprogress/DashboardMyProgressWidget.kt index b823bcb1ef..ddd322abbb 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/myprogress/DashboardMyProgressWidget.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/myprogress/DashboardMyProgressWidget.kt @@ -26,6 +26,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.instructure.horizon.R import com.instructure.horizon.features.dashboard.DashboardItemState import com.instructure.horizon.features.dashboard.widget.DashboardWidgetCardError +import com.instructure.horizon.features.dashboard.widget.DashboardWidgetPageState import com.instructure.horizon.features.dashboard.widget.myprogress.card.DashboardMyProgressCardContent import com.instructure.horizon.features.dashboard.widget.myprogress.card.DashboardMyProgressCardState import com.instructure.horizon.horizonui.foundation.HorizonColors @@ -36,6 +37,7 @@ import kotlinx.coroutines.flow.update fun DashboardMyProgressWidget( shouldRefresh: Boolean, refreshState: MutableStateFlow>, + pageState: DashboardWidgetPageState, modifier: Modifier = Modifier ) { val viewModel = hiltViewModel() @@ -50,12 +52,13 @@ fun DashboardMyProgressWidget( } } - DashboardMyProgressSection(state, modifier) + DashboardMyProgressSection(state, pageState, modifier) } @Composable fun DashboardMyProgressSection( state: DashboardMyProgressUiState, + pageState: DashboardWidgetPageState, modifier: Modifier = Modifier ) { when (state.state) { @@ -63,6 +66,7 @@ fun DashboardMyProgressSection( DashboardMyProgressCardContent( DashboardMyProgressCardState.Loading, true, + pageState, modifier ) } @@ -72,6 +76,7 @@ fun DashboardMyProgressSection( R.drawable.trending_up, HorizonColors.PrimitivesSky.sky12, false, + pageState, { state.onRefresh {} }, modifier = modifier ) @@ -80,6 +85,7 @@ fun DashboardMyProgressSection( DashboardMyProgressCardContent( state.cardState, false, + pageState, modifier ) } diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/myprogress/card/DashboardMyProgressCardContent.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/myprogress/card/DashboardMyProgressCardContent.kt index 750752613d..6f81951196 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/myprogress/card/DashboardMyProgressCardContent.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/myprogress/card/DashboardMyProgressCardContent.kt @@ -35,6 +35,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.instructure.horizon.R import com.instructure.horizon.features.dashboard.widget.DashboardWidgetCard +import com.instructure.horizon.features.dashboard.widget.DashboardWidgetPageState import com.instructure.horizon.horizonui.animation.shimmerEffect import com.instructure.horizon.horizonui.foundation.HorizonColors import com.instructure.horizon.horizonui.foundation.HorizonTypography @@ -44,12 +45,14 @@ import com.instructure.horizon.horizonui.foundation.HorizonTypography fun DashboardMyProgressCardContent( state: DashboardMyProgressCardState, isLoading: Boolean, + pageState: DashboardWidgetPageState, modifier: Modifier = Modifier, ) { DashboardWidgetCard( stringResource(R.string.dashboardMyProgressTitle), R.drawable.trending_up, HorizonColors.PrimitivesSky.sky12, + pageState = pageState, useMinWidth = false, isLoading = isLoading, modifier = modifier @@ -104,7 +107,8 @@ private fun DashboardMyProgressCardContentPreview() { state = DashboardMyProgressCardState( moduleCountCompleted = 24 ), - false + false, + DashboardWidgetPageState(1, 2) ) } @@ -115,7 +119,8 @@ private fun DashboardMyProgressCardContentZeroPreview() { state = DashboardMyProgressCardState( moduleCountCompleted = 0 ), - false + false, + DashboardWidgetPageState(1, 2) ) } @@ -126,6 +131,7 @@ private fun DashboardMyProgressLoadingPreview() { state = DashboardMyProgressCardState( moduleCountCompleted = 0 ), - isLoading = true + isLoading = true, + DashboardWidgetPageState(1, 2) ) } diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skillhighlights/DashboardSkillHighlightsWidget.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skillhighlights/DashboardSkillHighlightsWidget.kt index cd3d4d0cb8..06f56b29ce 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skillhighlights/DashboardSkillHighlightsWidget.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skillhighlights/DashboardSkillHighlightsWidget.kt @@ -29,6 +29,7 @@ import androidx.navigation.NavHostController import com.instructure.horizon.R import com.instructure.horizon.features.dashboard.DashboardItemState import com.instructure.horizon.features.dashboard.widget.DashboardWidgetCardError +import com.instructure.horizon.features.dashboard.widget.DashboardWidgetPageState import com.instructure.horizon.features.dashboard.widget.skillhighlights.card.DashboardSkillHighlightsCardContent import com.instructure.horizon.features.dashboard.widget.skillhighlights.card.DashboardSkillHighlightsCardState import com.instructure.horizon.horizonui.foundation.HorizonColors @@ -78,6 +79,7 @@ fun DashboardSkillHighlightsSection( R.drawable.hub, HorizonColors.PrimitivesGreen.green12(), false, + DashboardWidgetPageState.Empty, { state.onRefresh {} }, modifier = modifier.padding(horizontal = 24.dp) ) diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skillhighlights/card/DashboardSkillHighlightsCardContent.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skillhighlights/card/DashboardSkillHighlightsCardContent.kt index f3844d12d0..fd4e72a0a4 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skillhighlights/card/DashboardSkillHighlightsCardContent.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skillhighlights/card/DashboardSkillHighlightsCardContent.kt @@ -71,7 +71,6 @@ fun DashboardSkillHighlightsCardContent( ) { if (state.skills.isEmpty()) { Column { - HorizonSpace(SpaceSize.SPACE_8) Text( text = stringResource(R.string.dashboardSkillHighlightsNoDataTitle), style = HorizonTypography.h4, diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skilloverview/DashboardSkillOverviewWidget.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skilloverview/DashboardSkillOverviewWidget.kt index 2e21839cb3..785bf55d89 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skilloverview/DashboardSkillOverviewWidget.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skilloverview/DashboardSkillOverviewWidget.kt @@ -28,6 +28,7 @@ import androidx.navigation.NavHostController import com.instructure.horizon.R import com.instructure.horizon.features.dashboard.DashboardItemState import com.instructure.horizon.features.dashboard.widget.DashboardWidgetCardError +import com.instructure.horizon.features.dashboard.widget.DashboardWidgetPageState import com.instructure.horizon.features.dashboard.widget.skilloverview.card.DashboardSkillOverviewCardContent import com.instructure.horizon.features.dashboard.widget.skilloverview.card.DashboardSkillOverviewCardState import com.instructure.horizon.horizonui.foundation.HorizonColors @@ -39,6 +40,7 @@ fun DashboardSkillOverviewWidget( homeNavController: NavHostController, shouldRefresh: Boolean, refreshState: MutableStateFlow>, + pageState: DashboardWidgetPageState, modifier: Modifier = Modifier ) { val viewModel = hiltViewModel() @@ -53,12 +55,13 @@ fun DashboardSkillOverviewWidget( } } - DashboardSkillOverviewSection(state, homeNavController, modifier) + DashboardSkillOverviewSection(state, pageState, homeNavController, modifier) } @Composable fun DashboardSkillOverviewSection( state: DashboardSkillOverviewUiState, + pageState: DashboardWidgetPageState, homeNavController: NavHostController, modifier: Modifier = Modifier ) { @@ -68,6 +71,7 @@ fun DashboardSkillOverviewSection( DashboardSkillOverviewCardState.Loading, homeNavController, true, + pageState, modifier ) } @@ -77,6 +81,7 @@ fun DashboardSkillOverviewSection( R.drawable.hub, HorizonColors.PrimitivesGreen.green12(), false, + pageState, { state.onRefresh {} }, modifier = modifier ) @@ -86,6 +91,7 @@ fun DashboardSkillOverviewSection( state.cardState, homeNavController, false, + pageState, modifier ) } diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skilloverview/card/DashboardSkillOverviewCardContent.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skilloverview/card/DashboardSkillOverviewCardContent.kt index 77567167d4..5c2fef78de 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skilloverview/card/DashboardSkillOverviewCardContent.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/skilloverview/card/DashboardSkillOverviewCardContent.kt @@ -38,6 +38,7 @@ import androidx.navigation.compose.rememberNavController import com.instructure.canvasapi2.utils.ContextKeeper import com.instructure.horizon.R import com.instructure.horizon.features.dashboard.widget.DashboardWidgetCard +import com.instructure.horizon.features.dashboard.widget.DashboardWidgetPageState import com.instructure.horizon.features.home.HomeNavigationRoute import com.instructure.horizon.horizonui.animation.shimmerEffect import com.instructure.horizon.horizonui.foundation.HorizonColors @@ -48,12 +49,14 @@ fun DashboardSkillOverviewCardContent( state: DashboardSkillOverviewCardState, homeNavController: NavHostController, isLoading: Boolean, + pageState: DashboardWidgetPageState, modifier: Modifier = Modifier, ) { DashboardWidgetCard( title = stringResource(R.string.dashboardSkillOverviewTitle), iconRes = R.drawable.hub, widgetColor = HorizonColors.PrimitivesGreen.green12(), + pageState = pageState, isLoading = isLoading, useMinWidth = false, onClick = { @@ -114,7 +117,8 @@ private fun DashboardSkillOverviewCardContentPreview() { DashboardSkillOverviewCardContent( state = DashboardSkillOverviewCardState(completedSkillCount = 24), rememberNavController(), - false + false, + DashboardWidgetPageState(1, 2) ) } @@ -125,7 +129,8 @@ private fun DashboardSkillOverviewCardContentNoDataPreview() { DashboardSkillOverviewCardContent( state = DashboardSkillOverviewCardState(completedSkillCount = 0), rememberNavController(), - false + false, + DashboardWidgetPageState(1, 2) ) } @@ -136,7 +141,8 @@ private fun DashboardSkillOverviewLoadingPreview() { DashboardSkillOverviewCardContent( state = DashboardSkillOverviewCardState(completedSkillCount = 0), rememberNavController(), - isLoading = true + isLoading = true, + DashboardWidgetPageState(1, 2) ) } diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/timespent/DashboardTimeSpentWidget.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/timespent/DashboardTimeSpentWidget.kt index 00b3b3b662..da87ee767f 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/timespent/DashboardTimeSpentWidget.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/timespent/DashboardTimeSpentWidget.kt @@ -26,6 +26,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.instructure.horizon.R import com.instructure.horizon.features.dashboard.DashboardItemState import com.instructure.horizon.features.dashboard.widget.DashboardWidgetCardError +import com.instructure.horizon.features.dashboard.widget.DashboardWidgetPageState import com.instructure.horizon.features.dashboard.widget.timespent.card.DashboardTimeSpentCardContent import com.instructure.horizon.features.dashboard.widget.timespent.card.DashboardTimeSpentCardState import com.instructure.horizon.horizonui.foundation.HorizonColors @@ -36,6 +37,7 @@ import kotlinx.coroutines.flow.update fun DashboardTimeSpentWidget( shouldRefresh: Boolean, refreshState: MutableStateFlow>, + pageState: DashboardWidgetPageState, modifier: Modifier = Modifier ) { val viewModel = hiltViewModel() @@ -50,12 +52,13 @@ fun DashboardTimeSpentWidget( } } - DashboardTimeSpentSection(state, modifier) + DashboardTimeSpentSection(state, pageState, modifier) } @Composable fun DashboardTimeSpentSection( state: DashboardTimeSpentUiState, + pageState: DashboardWidgetPageState, modifier: Modifier = Modifier ) { when (state.state) { @@ -63,6 +66,7 @@ fun DashboardTimeSpentSection( DashboardTimeSpentCardContent( DashboardTimeSpentCardState.Loading, true, + pageState, modifier ) } @@ -72,6 +76,7 @@ fun DashboardTimeSpentSection( R.drawable.schedule, HorizonColors.PrimitivesHoney.honey12(), false, + pageState, { state.onRefresh {} }, modifier = modifier ) @@ -80,6 +85,7 @@ fun DashboardTimeSpentSection( DashboardTimeSpentCardContent( state.cardState, false, + pageState, modifier ) } diff --git a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/timespent/card/DashboardTimeSpentCardContent.kt b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/timespent/card/DashboardTimeSpentCardContent.kt index 89cd0a7d58..12dc48d1e1 100644 --- a/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/timespent/card/DashboardTimeSpentCardContent.kt +++ b/libs/horizon/src/main/java/com/instructure/horizon/features/dashboard/widget/timespent/card/DashboardTimeSpentCardContent.kt @@ -43,6 +43,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.instructure.horizon.R import com.instructure.horizon.features.dashboard.widget.DashboardWidgetCard +import com.instructure.horizon.features.dashboard.widget.DashboardWidgetPageState import com.instructure.horizon.horizonui.animation.shimmerEffect import com.instructure.horizon.horizonui.foundation.HorizonColors import com.instructure.horizon.horizonui.foundation.HorizonSpace @@ -57,6 +58,7 @@ import com.instructure.horizon.horizonui.organisms.inputs.singleselect.SingleSel fun DashboardTimeSpentCardContent( state: DashboardTimeSpentCardState, isLoading: Boolean, + pageState: DashboardWidgetPageState, modifier: Modifier = Modifier, ) { DashboardWidgetCard( @@ -64,6 +66,7 @@ fun DashboardTimeSpentCardContent( R.drawable.schedule, HorizonColors.PrimitivesHoney.honey12(), modifier, + pageState, isLoading, false ) { @@ -236,7 +239,8 @@ private fun DashboardTimeSpentCardContentPreview() { ), selectedCourseId = null ), - isLoading = false + isLoading = false, + DashboardWidgetPageState(1, 2) ) } @@ -253,7 +257,8 @@ private fun DashboardTimeSpentCardSelectedContentPreview() { ), selectedCourseId = 1 ), - isLoading = false + isLoading = false, + DashboardWidgetPageState(1, 2) ) } @@ -268,7 +273,8 @@ private fun DashboardTimeSpentCardContentSingleCourseHoursPreview() { ), selectedCourseId = null ), - isLoading = false + isLoading = false, + DashboardWidgetPageState(1, 2) ) } @@ -283,7 +289,8 @@ private fun DashboardTimeSpentCardContentSingleCourseMinutesPreview() { ), selectedCourseId = null ), - isLoading = false + isLoading = false, + DashboardWidgetPageState(1, 2) ) } @@ -299,7 +306,8 @@ private fun DashboardTimeSpentCardContentSingleCourseCombinedPreview() { ), selectedCourseId = null ), - isLoading = false + isLoading = false, + DashboardWidgetPageState(1, 2) ) } @@ -313,7 +321,8 @@ private fun DashboardTimeSpentCardEmptyContentPreview() { ), selectedCourseId = null ), - isLoading = false + isLoading = false, + DashboardWidgetPageState(1, 2) ) } @@ -322,6 +331,7 @@ private fun DashboardTimeSpentCardEmptyContentPreview() { private fun DashboardTimeSpentCardLoadingPreview() { DashboardTimeSpentCardContent( state = DashboardTimeSpentCardState(), - isLoading = true + isLoading = true, + DashboardWidgetPageState(1, 2) ) } diff --git a/libs/horizon/src/main/res/values/strings.xml b/libs/horizon/src/main/res/values/strings.xml index d947e10fd1..b13d1b84df 100644 --- a/libs/horizon/src/main/res/values/strings.xml +++ b/libs/horizon/src/main/res/values/strings.xml @@ -439,5 +439,5 @@ Refresh Next item Previous item - %1$d of %2$d + %1$d of %2$d \ No newline at end of file diff --git a/libs/horizon/src/test/java/com/instructure/horizon/features/dashboard/course/DashboardCourseViewModelTest.kt b/libs/horizon/src/test/java/com/instructure/horizon/features/dashboard/course/DashboardCourseViewModelTest.kt index 9c34deee31..b383942e32 100644 --- a/libs/horizon/src/test/java/com/instructure/horizon/features/dashboard/course/DashboardCourseViewModelTest.kt +++ b/libs/horizon/src/test/java/com/instructure/horizon/features/dashboard/course/DashboardCourseViewModelTest.kt @@ -28,10 +28,13 @@ import com.instructure.horizon.features.dashboard.widget.course.DashboardCourseR import com.instructure.horizon.features.dashboard.widget.course.DashboardCourseViewModel import com.instructure.journey.type.ProgramProgressCourseEnrollmentStatus import com.instructure.journey.type.ProgramVariantType +import com.instructure.pandautils.utils.ThemePrefs import io.mockk.coEvery import io.mockk.coVerify +import io.mockk.every import io.mockk.just import io.mockk.mockk +import io.mockk.mockkObject import io.mockk.runs import io.mockk.unmockkAll import junit.framework.TestCase.assertEquals @@ -167,6 +170,9 @@ class DashboardCourseViewModelTest { fun setup() { Dispatchers.setMain(testDispatcher) + mockkObject(ThemePrefs) + every { ThemePrefs.brandColor } returns 1 + coEvery { repository.getEnrollments(any()) } returns activeEnrollments + invitedEnrollments + completedEnrollments coEvery { repository.getPrograms(any()) } returns programs coEvery { repository.acceptInvite(any(), any()) } just runs diff --git a/libs/horizon/src/test/java/com/instructure/horizon/features/dashboard/widget/announcement/DashboardAnnouncementBannerViewModelTest.kt b/libs/horizon/src/test/java/com/instructure/horizon/features/dashboard/widget/announcement/DashboardAnnouncementBannerViewModelTest.kt index e787f2b8c2..8aefd97392 100644 --- a/libs/horizon/src/test/java/com/instructure/horizon/features/dashboard/widget/announcement/DashboardAnnouncementBannerViewModelTest.kt +++ b/libs/horizon/src/test/java/com/instructure/horizon/features/dashboard/widget/announcement/DashboardAnnouncementBannerViewModelTest.kt @@ -17,12 +17,16 @@ package com.instructure.horizon.features.dashboard.widget.announcement import android.content.Context +import com.instructure.horizon.R import com.instructure.horizon.features.dashboard.DashboardEventHandler import com.instructure.horizon.features.dashboard.DashboardItemState import com.instructure.horizon.features.dashboard.widget.DashboardPaginatedWidgetCardButtonRoute +import com.instructure.pandautils.utils.ThemePrefs import io.mockk.coEvery import io.mockk.coVerify +import io.mockk.every import io.mockk.mockk +import io.mockk.mockkObject import io.mockk.unmockkAll import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertTrue @@ -47,6 +51,9 @@ class DashboardAnnouncementBannerViewModelTest { @Before fun setup() { + mockkObject(ThemePrefs) + every { ThemePrefs.brandColor } returns 1 + every { context.getString(R.string.notificationsAnnouncementCategoryLabel) } returns "Announcement" Dispatchers.setMain(testDispatcher) }