Add apps to k5 course resources tab
This loads the non-hidden, course nav apps for the k5 subject course on the resources tab. closes LS-2062 flag=canvas_for_elementary Test plan: - Open up a k5 subject course as a teacher or student - Click the resources tab - Expect to see whatever apps appear for that user on the classic course nav - Click an app, expect app to open in course context - Reload page, expect to see loading spinner briefly - Simulate network failure, expect to see error message when loading apps Change-Id: Ie483315d02feaeaeea835656dacf2739902ee2ef Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/262572 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Jonathan Guardado <jonathan.guardado@instructure.com> QA-Review: Jonathan Guardado <jonathan.guardado@instructure.com> Product-Review: Peyton Craighill <pcraighill@instructure.com>
This commit is contained in:
parent
86c6f99285
commit
e1ccc28388
|
@ -30,6 +30,7 @@ ready(() => {
|
|||
<K5Course
|
||||
currentUser={ENV.current_user}
|
||||
name={ENV.COURSE.name}
|
||||
id={ENV.COURSE.id}
|
||||
imageUrl={ENV.COURSE.image_url}
|
||||
plannerEnabled={ENV.STUDENT_PLANNER_ENABLED}
|
||||
timeZone={ENV.TIMEZONE}
|
||||
|
|
|
@ -29,7 +29,8 @@ import {
|
|||
IconCalendarMonthLine,
|
||||
IconHomeLine,
|
||||
IconModuleLine,
|
||||
IconStarLightLine
|
||||
IconStarLightLine,
|
||||
IconBankLine
|
||||
} from '@instructure/ui-icons'
|
||||
import {ApplyTheme} from '@instructure/ui-themeable'
|
||||
import {Heading} from '@instructure/ui-heading'
|
||||
|
@ -42,8 +43,10 @@ import SchedulePage from '@canvas/k5/react/SchedulePage'
|
|||
import usePlanner from '@canvas/k5/react/hooks/usePlanner'
|
||||
import useTabState from '@canvas/k5/react/hooks/useTabState'
|
||||
import {mapStateToProps} from '@canvas/k5/redux/redux-helpers'
|
||||
import {TAB_IDS} from '@canvas/k5/react/utils'
|
||||
import {TAB_IDS, fetchCourseApps} from '@canvas/k5/react/utils'
|
||||
import k5Theme, {theme} from '@canvas/k5/react/k5-theme'
|
||||
import AppsList from '@canvas/k5/react/AppsList'
|
||||
import {showFlashError} from '@canvas/alerts/react/FlashAlert'
|
||||
|
||||
const DEFAULT_COLOR = k5Theme.variables.colors.backgroundMedium
|
||||
const HERO_HEIGHT_PX = 400
|
||||
|
@ -68,6 +71,11 @@ const COURSE_TABS = [
|
|||
id: TAB_IDS.GRADES,
|
||||
icon: IconStarLightLine,
|
||||
label: I18n.t('Grades')
|
||||
},
|
||||
{
|
||||
id: TAB_IDS.RESOURCES,
|
||||
icon: IconBankLine,
|
||||
label: I18n.t('Resources')
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -108,6 +116,16 @@ export function CourseHeaderHero({name, image, backgroundColor}) {
|
|||
)
|
||||
}
|
||||
|
||||
const fetchApps = (courseId, courseName) =>
|
||||
fetchCourseApps(courseId).then(apps =>
|
||||
apps.map(app => ({
|
||||
id: app.id,
|
||||
courses: [{id: courseId, name: courseName}],
|
||||
title: app.course_navigation.text || app.name,
|
||||
icon: app.course_navigation.icon_url || app.icon_url
|
||||
}))
|
||||
)
|
||||
|
||||
export function K5Course({
|
||||
assignmentsDueToday,
|
||||
assignmentsMissing,
|
||||
|
@ -115,6 +133,7 @@ export function K5Course({
|
|||
imageUrl,
|
||||
loadAllOpportunities,
|
||||
name,
|
||||
id,
|
||||
timeZone,
|
||||
defaultTab = TAB_IDS.OVERVIEW,
|
||||
plannerEnabled = false
|
||||
|
@ -128,6 +147,8 @@ export function K5Course({
|
|||
callback: () => loadAllOpportunities(),
|
||||
singleCourse: true
|
||||
})
|
||||
const [apps, setApps] = useState([])
|
||||
const [isAppsLoading, setAppsLoading] = useState(false)
|
||||
|
||||
const modulesRef = useRef(null)
|
||||
useEffect(() => {
|
||||
|
@ -140,6 +161,14 @@ export function K5Course({
|
|||
}
|
||||
}, [currentTab])
|
||||
|
||||
useEffect(() => {
|
||||
setAppsLoading(true)
|
||||
fetchApps(id, name)
|
||||
.then(setApps)
|
||||
.catch(showFlashError(I18n.t('Failed to load apps for %{name}.', {name})))
|
||||
.finally(() => setAppsLoading(false))
|
||||
}, [id, name])
|
||||
|
||||
return (
|
||||
<K5DashboardContext.Provider
|
||||
value={{
|
||||
|
@ -160,6 +189,7 @@ export function K5Course({
|
|||
</K5Tabs>
|
||||
{plannerInitialized && <SchedulePage visible={currentTab === TAB_IDS.SCHEDULE} />}
|
||||
{!plannerEnabled && currentTab === TAB_IDS.SCHEDULE && createTeacherPreview(timeZone)}
|
||||
{currentTab === TAB_IDS.RESOURCES && <AppsList isLoading={isAppsLoading} apps={apps} />}
|
||||
</View>
|
||||
</K5DashboardContext.Provider>
|
||||
)
|
||||
|
@ -171,6 +201,7 @@ K5Course.propTypes = {
|
|||
assignmentsCompletedForToday: PropTypes.object.isRequired,
|
||||
loadAllOpportunities: PropTypes.func.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
id: PropTypes.string.isRequired,
|
||||
timeZone: PropTypes.string.isRequired,
|
||||
defaultTab: PropTypes.string,
|
||||
imageUrl: PropTypes.string,
|
||||
|
|
|
@ -20,6 +20,8 @@ import React from 'react'
|
|||
import moxios from 'moxios'
|
||||
import {render, waitFor} from '@testing-library/react'
|
||||
import {K5Course} from '../K5Course'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import {TAB_IDS} from '@canvas/k5/react/utils'
|
||||
|
||||
const currentUser = {
|
||||
id: '1',
|
||||
|
@ -42,15 +44,28 @@ const defaultProps = {
|
|||
currentUser,
|
||||
loadAllOpportunities: () => {},
|
||||
name: 'Arts and Crafts',
|
||||
id: '30',
|
||||
timeZone: defaultEnv.TIMEZONE
|
||||
}
|
||||
const fetchAppsResponse = [
|
||||
{
|
||||
id: '7',
|
||||
course_navigation: {
|
||||
text: 'Studio',
|
||||
icon_url: 'studio.png'
|
||||
}
|
||||
}
|
||||
]
|
||||
const FETCH_APPS_URL = '/api/v1/courses/30/external_tools/visible_course_nav_tools'
|
||||
|
||||
beforeAll(() => {
|
||||
moxios.install()
|
||||
fetchMock.get(FETCH_APPS_URL, JSON.stringify(fetchAppsResponse))
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
moxios.uninstall()
|
||||
fetchMock.restore()
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -84,9 +99,9 @@ describe('K-5 Subject Course', () => {
|
|||
expect(getByText(defaultProps.name)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows Overview, Schedule, Modules, and Grades options', () => {
|
||||
it('shows Overview, Schedule, Modules, Grades, and Resources options', () => {
|
||||
const {getByText} = render(<K5Course {...defaultProps} />)
|
||||
;['Overview', 'Schedule', 'Modules', 'Grades'].forEach(label =>
|
||||
;['Overview', 'Schedule', 'Modules', 'Grades', 'Resources'].forEach(label =>
|
||||
expect(getByText(label)).toBeInTheDocument()
|
||||
)
|
||||
})
|
||||
|
@ -108,4 +123,30 @@ describe('K-5 Subject Course', () => {
|
|||
waitFor(() => expect(modulesContainer.style.display).toBe('block'))
|
||||
})
|
||||
})
|
||||
|
||||
describe('resources tab', () => {
|
||||
it("displays user's apps", () => {
|
||||
const {getByText} = render(<K5Course {...defaultProps} defaultTab={TAB_IDS.RESOURCES} />)
|
||||
waitFor(() => {
|
||||
expect(getByText('Studio')).toBeInTheDocument()
|
||||
expect(getByText('Student Applications')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('shows a loading spinner while apps are loading', () => {
|
||||
const {getByText, queryByText} = render(
|
||||
<K5Course {...defaultProps} defaultTab={TAB_IDS.RESOURCES} />
|
||||
)
|
||||
waitFor(() => expect(getByText('Loading apps...')).toBeInTheDocument())
|
||||
expect(queryByText('Studio')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows an error if apps fail to load', () => {
|
||||
fetchMock.get(FETCH_APPS_URL, 400, {overwriteRoutes: true})
|
||||
const {getByText} = render(<K5Course {...defaultProps} defaultTab={TAB_IDS.RESOURCES} />)
|
||||
waitFor(() =>
|
||||
expect(getByText('Failed to load apps for Arts and Crafts.')).toBeInTheDocument()
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -21,7 +21,7 @@ import React, {useState, useEffect} from 'react'
|
|||
import PropTypes from 'prop-types'
|
||||
import StaffContactInfoLayout from './StaffContactInfoLayout'
|
||||
import {fetchCourseInstructors, fetchCourseApps} from '@canvas/k5/react/utils'
|
||||
import AppsList from './AppsList'
|
||||
import AppsList from '@canvas/k5/react/AppsList'
|
||||
import {showFlashError} from '@canvas/alerts/react/FlashAlert'
|
||||
|
||||
const fetchStaff = cards =>
|
||||
|
|
|
@ -28,7 +28,7 @@ import {TruncateText} from '@instructure/ui-truncate-text'
|
|||
import {Tooltip} from '@instructure/ui-tooltip'
|
||||
import {IconLtiSolid} from '@instructure/ui-icons'
|
||||
import {PresentationContent} from '@instructure/ui-a11y-content'
|
||||
import k5Theme from '@canvas/k5/react/k5-theme'
|
||||
import k5Theme from './k5-theme'
|
||||
import {Flex} from '@instructure/ui-flex'
|
||||
|
||||
export default function K5AppLink({app}) {
|
Loading…
Reference in New Issue