Skip to content

Commit 61829ad

Browse files
EssWhyykokrui
andauthored
Grey Out Deprecated Courses In PreReq Tree (#3647)
* Bind ModuleTree to Redux State * Connect Selector to Tree and Branch Props * Cleanup * Use of style conditionals to avoid code duplication * Fix prettier errors * Update previous tests and ModuleTree -> ModuleTreeComponent * Add tests for new change, update snapshots --------- Co-authored-by: kokrui <[email protected]>
1 parent 1ec2b2b commit 61829ad

File tree

3 files changed

+429
-53
lines changed

3 files changed

+429
-53
lines changed

website/src/views/modules/ModuleTree.test.tsx

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import { render } from 'enzyme';
22

3-
import ModuleTree from './ModuleTree';
3+
import { getModuleCondensed } from 'selectors/moduleBank';
4+
import { ModuleCondensed } from 'types/modules';
5+
import { ModuleTreeComponent } from './ModuleTree';
46

57
jest.mock('views/components/LinkModuleCodes', () => 'mockedlink');
68

7-
describe(ModuleTree, () => {
9+
describe(ModuleTreeComponent, () => {
810
test('should render requirements fulfilled tree of module', () => {
911
const component = render(
10-
<ModuleTree
12+
<ModuleTreeComponent
1113
moduleCode="ACC1002"
14+
getModuleCondensed={getModuleCondensed({ moduleBank: { moduleCodes: {} } } as any)}
1215
fulfillRequirements={[
1316
'ACC1006',
1417
'ACC2002',
@@ -33,8 +36,9 @@ describe(ModuleTree, () => {
3336

3437
test('should render prereq tree of module', () => {
3538
const component = render(
36-
<ModuleTree
39+
<ModuleTreeComponent
3740
moduleCode="CS3244"
41+
getModuleCondensed={getModuleCondensed({ moduleBank: { moduleCodes: {} } } as any)}
3842
fulfillRequirements={['CS5242', 'CS5339', 'CS6281']}
3943
prereqTree={{
4044
and: [
@@ -57,4 +61,68 @@ describe(ModuleTree, () => {
5761

5862
expect(component).toMatchSnapshot('CS3244');
5963
});
64+
65+
// Test that modules which are in moduleBank have appropriate colours,
66+
// and modules that aren't are greyed out
67+
68+
const testModules: { [moduleCode: string]: ModuleCondensed } = {
69+
CS2040: {
70+
moduleCode: 'CS2040',
71+
title: 'Data Structures and Algorithms',
72+
semesters: [1, 2],
73+
},
74+
CS2030: {
75+
moduleCode: 'CS2030',
76+
title: 'Programming Methodology II',
77+
semesters: [1, 2],
78+
},
79+
CS2113T: {
80+
moduleCode: 'CS2113T',
81+
title: 'Software Engineering & Object-Oriented Programming',
82+
semesters: [1, 2],
83+
},
84+
CS1020E: {
85+
moduleCode: 'CS1020E',
86+
title: 'Data Structures and Algorithms',
87+
semesters: [1, 2],
88+
},
89+
CS6240: {
90+
moduleCode: 'CS6240',
91+
title: 'Multimedia Analysis',
92+
semesters: [2],
93+
},
94+
};
95+
96+
test('should grey out modules that are not in module bank', () => {
97+
const component = render(
98+
<ModuleTreeComponent
99+
moduleCode="CS4243"
100+
getModuleCondensed={getModuleCondensed({ moduleBank: { moduleCodes: testModules } } as any)}
101+
fulfillRequirements={['CS6240', 'CS3281', 'CS4243R']}
102+
prereqTree={{
103+
and: [
104+
{
105+
or: [
106+
'CS1020',
107+
'CS1020E',
108+
'CS2020',
109+
{
110+
and: [
111+
{
112+
or: ['CS2030', 'CS2113', 'CS2113T'],
113+
},
114+
{
115+
or: ['CS2040', 'CS2040C'],
116+
},
117+
],
118+
},
119+
],
120+
},
121+
],
122+
}}
123+
/>,
124+
);
125+
126+
expect(component).toMatchSnapshot('CS4243');
127+
});
60128
});

website/src/views/modules/ModuleTree.tsx

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,31 @@
11
import * as React from 'react';
22
import classnames from 'classnames';
33
import { flatten, values } from 'lodash';
4-
import LinkModuleCodes from 'views/components/LinkModuleCodes';
4+
import { connect } from 'react-redux';
55

6-
import { ModuleCode, PrereqTree } from 'types/modules';
6+
import { getModuleCondensed } from 'selectors/moduleBank';
77

8+
import { ModuleCode, PrereqTree, ModuleCondensed } from 'types/modules';
9+
import { State } from 'types/state';
810
import { notNull } from 'types/utils';
11+
12+
import LinkModuleCodes from 'views/components/LinkModuleCodes';
913
import styles from './ModuleTree.scss';
1014

1115
type Props = {
1216
moduleCode: ModuleCode;
1317
fulfillRequirements?: readonly ModuleCode[];
1418
prereqTree?: PrereqTree;
19+
20+
getModuleCondensed: (moduleCode: ModuleCode) => ModuleCondensed | undefined;
1521
};
1622

1723
interface TreeDisplay {
1824
layer: number;
1925
node: PrereqTree;
2026
isPrereq?: boolean;
27+
28+
getModuleCondensed: (moduleCode: ModuleCode) => ModuleCondensed | undefined;
2129
}
2230

2331
const GRADE_REQUIREMENT_SEPARATOR = ':';
@@ -36,7 +44,10 @@ const formatConditional = (node: PrereqTree) => {
3644
return 'all of';
3745
};
3846

39-
type NodeName = { prefix?: string; name: string };
47+
type NodeName = {
48+
prefix?: string;
49+
name: string;
50+
};
4051
const nodeName = (node: PrereqTree): NodeName => {
4152
if (typeof node !== 'string') {
4253
return { name: Object.keys(node)[0] };
@@ -69,11 +80,15 @@ const unwrapLayer = (node: PrereqTree) => {
6980
return flatten(values(node).filter(notNull));
7081
};
7182

72-
const Branch: React.FC<{ nodes: PrereqTree[]; layer: number }> = (props) => (
83+
const Branch: React.FC<{
84+
nodes: PrereqTree[];
85+
layer: number;
86+
getModuleCondensed: (moduleCode: ModuleCode) => ModuleCondensed | undefined;
87+
}> = (props) => (
7388
<ul className={styles.tree}>
7489
{props.nodes.map((child, idx) => (
7590
<li className={styles.branch} key={typeof child === 'string' ? nodeName(child).name : idx}>
76-
<Tree node={child} layer={props.layer} />
91+
<Tree node={child} layer={props.layer} getModuleCondensed={props.getModuleCondensed} />
7792
</li>
7893
))}
7994
</ul>
@@ -95,14 +110,24 @@ const Tree: React.FC<TreeDisplay> = (props) => {
95110
>
96111
{formatConditional(node)}
97112
</div>
98-
<Branch nodes={unwrapLayer(node)} layer={layer + 1} />
113+
<Branch
114+
nodes={unwrapLayer(node)}
115+
layer={layer + 1}
116+
getModuleCondensed={props.getModuleCondensed}
117+
/>
99118
</>
100119
);
101120
}
102121

122+
// Check if module name still exists in database
123+
const moduleActive = props.getModuleCondensed(name);
124+
125+
// If module is deprecated (undefined) then we grey out, remove color classname
126+
103127
return (
104128
<div
105-
className={classnames(styles.node, styles.moduleNode, `hoverable color-${layer}`, {
129+
className={classnames(styles.node, styles.moduleNode, {
130+
[`hoverable color-${layer}`]: !!moduleActive,
106131
[styles.prereqNode]: isPrereq,
107132
})}
108133
>
@@ -112,7 +137,7 @@ const Tree: React.FC<TreeDisplay> = (props) => {
112137
);
113138
};
114139

115-
const ModuleTree: React.FC<Props> = (props) => {
140+
export const ModuleTreeComponent: React.FC<Props> = (props) => {
116141
const { fulfillRequirements, prereqTree, moduleCode } = props;
117142

118143
return (
@@ -126,7 +151,12 @@ const ModuleTree: React.FC<Props> = (props) => {
126151
key={fulfilledModule}
127152
className={classnames(styles.branch, styles.prereqBranch)}
128153
>
129-
<Tree layer={0} node={fulfilledModule} isPrereq />
154+
<Tree
155+
layer={0}
156+
node={fulfilledModule}
157+
isPrereq
158+
getModuleCondensed={props.getModuleCondensed}
159+
/>
130160
</li>
131161
))}
132162
</ul>
@@ -137,9 +167,15 @@ const ModuleTree: React.FC<Props> = (props) => {
137167

138168
<ul className={classnames(styles.tree, styles.root)}>
139169
<li className={classnames(styles.branch)}>
140-
<Tree layer={1} node={moduleCode} />
141-
142-
{prereqTree && <Branch nodes={[prereqTree]} layer={2} />}
170+
<Tree layer={1} node={moduleCode} getModuleCondensed={props.getModuleCondensed} />
171+
172+
{prereqTree && (
173+
<Branch
174+
nodes={[prereqTree]}
175+
layer={2}
176+
getModuleCondensed={props.getModuleCondensed}
177+
/>
178+
)}
143179
</li>
144180
</ul>
145181
</div>
@@ -170,4 +206,8 @@ const ModuleTree: React.FC<Props> = (props) => {
170206
);
171207
};
172208

173-
export default ModuleTree;
209+
const mapStateToProps = connect((state: State) => ({
210+
getModuleCondensed: getModuleCondensed(state),
211+
}));
212+
213+
export default mapStateToProps(ModuleTreeComponent);

0 commit comments

Comments
 (0)