Skip to content

Commit 25d5ddc

Browse files
remo5000ning-y
authored andcommitted
Abstract Assessment overviews (#119)
* Change Missions component and prop names * Use proper component name in container * Change name in component to listing * Use assessmentId for Assessment Params * Change OverviewCard prop name * Change css names * Move assessmentlisting css * Rename and move Missions -> AssessmentListing * Update test naming and location * Add OwnProps for AssessmentListing Also changed an import for academy/index * Pass assessmentCategory prop to AssessmentListing * Add render methods for all assessment types * Format and modify tests * Change routing for mission assessment * Move assessment container files into a folder If there is a better way, we should abandon relative addressing
1 parent 84fb680 commit 25d5ddc

File tree

10 files changed

+235
-204
lines changed

10 files changed

+235
-204
lines changed

src/components/academy/__tests__/Missions.tsx

Lines changed: 0 additions & 54 deletions
This file was deleted.

src/components/academy/index.tsx

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
/* tslint:disable: jsx-no-lambda */
2+
13
import * as qs from 'query-string'
24
import * as React from 'react'
35
import { Redirect, Route, RouteComponentProps, Switch } from 'react-router'
46

5-
import MissionsContainer from '../../containers/academy/MissionsContainer'
7+
import AssessmentListingContainer from '../../containers/assessment/AssessmentListingContainer'
68
import Game from '../../containers/GameContainer'
79
import { isAcademyRe } from '../../reducers/session'
810
import { HistoryHelper } from '../../utils/history'
11+
import { AssessmentCategories, AssessmentCategory } from '../assessment/assessmentShape'
912
import AcademyNavigationBar from './NavigationBar'
1013

1114
interface IAcademyProps extends IDispatchProps, IOwnProps, IStateProps, RouteComponentProps<{}> {}
@@ -23,17 +26,37 @@ export interface IStateProps {
2326
historyHelper: HistoryHelper
2427
}
2528

29+
const assessmentListingRenderFactory = (cat: AssessmentCategory) => (
30+
routerProps: RouteComponentProps<any>
31+
) => <AssessmentListingContainer assessmentCategory={cat} />
32+
2633
export const Academy: React.SFC<IAcademyProps> = props => (
2734
<div className="Academy">
2835
<AcademyNavigationBar />
2936
<Switch>
3037
{checkLoggedIn(props)}
31-
<Route path="/academy/contests" component={MissionsContainer} />
38+
<Route
39+
path="/academy/contests"
40+
render={assessmentListingRenderFactory(AssessmentCategories.CONTEST)}
41+
/>
3242
<Route path="/academy/game" component={Game} />
33-
<Route exact={true} path="/academy/missions" component={MissionsContainer} />
34-
<Route path="/academy/missions/:missionId" component={MissionsContainer} />
35-
<Route path="/academy/paths" component={MissionsContainer} />
36-
<Route path="/academy/sidequests" component={MissionsContainer} />
43+
<Route
44+
exact={true}
45+
path="/academy/missions"
46+
render={assessmentListingRenderFactory(AssessmentCategories.MISSION)}
47+
/>
48+
<Route
49+
path="/academy/missions/:assessmentId"
50+
render={assessmentListingRenderFactory(AssessmentCategories.MISSION)}
51+
/>
52+
<Route
53+
path="/academy/paths"
54+
render={assessmentListingRenderFactory(AssessmentCategories.PATH)}
55+
/>
56+
<Route
57+
path="/academy/sidequests"
58+
render={assessmentListingRenderFactory(AssessmentCategories.SIDEQUEST)}
59+
/>
3760
<Route exact={true} path="/academy" component={dynamicRedirect(props)} />
3861
<Route component={redirectTo404} />
3962
</Switch>

src/components/academy/Missions.tsx renamed to src/components/assessment/AssessmentListing.tsx

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,50 @@ import * as React from 'react'
44
import { RouteComponentProps } from 'react-router'
55
import { NavLink } from 'react-router-dom'
66

7-
import AssessmentContainer from '../../containers/AssessmentContainer'
7+
import AssessmentContainer from '../../containers/assessment'
88
import { OwnProps as AssessmentProps } from '../assessment'
9+
import { AssessmentCategory } from '../assessment/assessmentShape'
910
import { IAssessmentOverview } from '../assessment/assessmentShape'
1011
import ContentDisplay, { IContentDisplayProps } from '../commons/ContentDisplay'
1112

12-
export interface IMissionParams {
13-
missionId?: string
13+
export interface IAssessmentParams {
14+
assessmentId?: string
1415
}
1516

16-
export interface IMissionsProps extends RouteComponentProps<IMissionParams> {
17+
export interface IAssessmentListingProps extends RouteComponentProps<IAssessmentParams> {
1718
assessmentOverviews?: IAssessmentOverview[]
19+
assessmentCategory: AssessmentCategory
1820
handleAssessmentOverviewFetch: () => void
1921
}
2022

21-
export type StateProps = Pick<IMissionsProps, 'assessmentOverviews'>
22-
export type DispatchProps = Pick<IMissionsProps, 'handleAssessmentOverviewFetch'>
23+
export type DispatchProps = Pick<IAssessmentListingProps, 'handleAssessmentOverviewFetch'>
24+
export type OwnProps = Pick<IAssessmentListingProps, 'assessmentCategory'>
25+
export type StateProps = Pick<IAssessmentListingProps, 'assessmentOverviews'>
2326

24-
class Missions extends React.Component<IMissionsProps, {}> {
27+
class AssessmentListing extends React.Component<IAssessmentListingProps, {}> {
2528
public render() {
26-
// make missionIdParam a number
27-
let missionIdParam: number | null =
28-
this.props.match.params.missionId === undefined
29+
// make assessmentId a number
30+
let assessmentIdParam: number | null =
31+
this.props.match.params.assessmentId === undefined
2932
? NaN
30-
: parseInt(this.props.match.params.missionId, 10)
33+
: parseInt(this.props.match.params.assessmentId, 10)
3134
// set as null if the parsing failed
32-
missionIdParam = Number.isInteger(missionIdParam) ? missionIdParam : null
35+
assessmentIdParam = Number.isInteger(assessmentIdParam) ? assessmentIdParam : null
3336

34-
// if there is no mission specified, Render only information.
35-
if (missionIdParam === null) {
37+
// if there is no assessmentId specified, Render only information.
38+
if (assessmentIdParam === null) {
3639
const props: IContentDisplayProps = {
3740
display: <AssessmentOverviewCard assessmentOverviews={this.props.assessmentOverviews} />,
3841
loadContentDispatch: this.props.handleAssessmentOverviewFetch
3942
}
4043
return (
41-
<div className="Missions">
44+
<div className="AssessmentListing">
4245
<ContentDisplay {...props} />
4346
</div>
4447
)
4548
} else {
4649
const props: AssessmentProps = {
47-
missionId: missionIdParam
50+
assessmentId: assessmentIdParam
4851
}
4952
return <AssessmentContainer {...props} />
5053
}
@@ -61,31 +64,31 @@ export const AssessmentOverviewCard: React.SFC<IAssessmentOverviewCardProps> = p
6164
} else if (props.assessmentOverviews.length === 0) {
6265
return <NonIdealState title="There are no assessments." visual={IconNames.FLAME} />
6366
}
64-
const cards = props.assessmentOverviews.map((mission, index) => (
67+
const cards = props.assessmentOverviews.map((overview, index) => (
6568
<div key={index}>
66-
<Card className="row mission-info">
67-
<div className="col-xs-3 mission-info-picture">PICTURE</div>
68-
<div className="col-xs-9 mission-info-text">
69-
<div className="row mission-info-title">
70-
<h4>{mission.title}</h4>
69+
<Card className="row listing">
70+
<div className="col-xs-3 listing-picture">PICTURE</div>
71+
<div className="col-xs-9 listing-text">
72+
<div className="row listing-title">
73+
<h4>{overview.title}</h4>
7174
</div>
72-
<div className="row mission-info-order">
75+
<div className="row listing-order">
7376
<h6>Mission 0 : 123123 XP (hardcoded)</h6>
7477
</div>
75-
<div className="row mission-info-description">
76-
<p className="col-xs-12">{mission.shortSummary}</p>
78+
<div className="row listing-description">
79+
<p className="col-xs-12">{overview.shortSummary}</p>
7780
</div>
78-
<div className="row between-xs middle-xs mission-info-controls">
79-
<div className="col-xs-8 mission-info-due-date-parent">
80-
<Text className="mission-info-due-date">
81-
<Icon className="mission-info-due-icon" iconSize={14} icon={IconNames.TIME} />
81+
<div className="row between-xs middle-xs listing-controls">
82+
<div className="col-xs-8 listing-due-date-parent">
83+
<Text className="listing-due-date">
84+
<Icon className="listing-due-icon" iconSize={14} icon={IconNames.TIME} />
8285
Due: 12/12/12
8386
</Text>
8487
</div>
8588
<div className="col-xs">
86-
<NavLink to={`/academy/missions/${mission.id.toString()}`}>
89+
<NavLink to={`/academy/missions/${overview.id.toString()}`}>
8790
<Button
88-
className="mission-info-skip-button"
91+
className="listing-skip-button"
8992
minimal={true}
9093
intent={Intent.PRIMARY}
9194
icon={IconNames.FLAME}
@@ -102,4 +105,4 @@ export const AssessmentOverviewCard: React.SFC<IAssessmentOverviewCardProps> = p
102105
return <>{cards}</>
103106
}
104107

105-
export default Missions
108+
export default AssessmentListing
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { mount } from 'enzyme'
2+
import * as React from 'react'
3+
import { MemoryRouter } from 'react-router'
4+
5+
import { mockAssessmentOverviews } from '../../../mocks/api'
6+
import { mockRouterProps } from '../../../mocks/components'
7+
import AssessmentListing, { IAssessmentListingProps } from '../AssessmentListing'
8+
import { AssessmentCategories } from '../assessmentShape'
9+
10+
const mockUndefinedAssessmentListing: IAssessmentListingProps = {
11+
...mockRouterProps('/academy/missions', {}),
12+
handleAssessmentOverviewFetch: () => {},
13+
assessmentCategory: AssessmentCategories.MISSION
14+
}
15+
16+
const mockEmptyAssessmentListing: IAssessmentListingProps = {
17+
...mockRouterProps('/academy/missions', {}),
18+
assessmentOverviews: [],
19+
handleAssessmentOverviewFetch: () => {},
20+
assessmentCategory: AssessmentCategories.MISSION
21+
}
22+
23+
const mockPresentAssessmentListing: IAssessmentListingProps = {
24+
...mockRouterProps('/academy/missions', {}),
25+
assessmentOverviews: mockAssessmentOverviews,
26+
handleAssessmentOverviewFetch: () => {},
27+
assessmentCategory: AssessmentCategories.MISSION
28+
}
29+
30+
test('AssessmentListing page "loading" content renders correctly', () => {
31+
const app = (
32+
<MemoryRouter initialEntries={['/unknown']}>
33+
<AssessmentListing {...mockUndefinedAssessmentListing} />
34+
</MemoryRouter>
35+
)
36+
const tree = mount(app)
37+
expect(tree.debug()).toMatchSnapshot()
38+
})
39+
40+
test('AssessmentListing page with 0 missions renders correctly', () => {
41+
const app = (
42+
<MemoryRouter initialEntries={['/unknown']}>
43+
<AssessmentListing {...mockEmptyAssessmentListing} />
44+
</MemoryRouter>
45+
)
46+
const tree = mount(app)
47+
expect(tree.debug()).toMatchSnapshot()
48+
})
49+
50+
test('AssessmentListing page with multiple loaded missions renders correctly', () => {
51+
const app = (
52+
<MemoryRouter initialEntries={['/unknown']}>
53+
<AssessmentListing {...mockPresentAssessmentListing} />
54+
</MemoryRouter>
55+
)
56+
const tree = mount(app)
57+
expect(tree.debug()).toMatchSnapshot()
58+
})

0 commit comments

Comments
 (0)