Skip to content

Commit d22710a

Browse files
authored
an update (#501)
* Removed previous button on first question, added divider * Yarn format * Changeded help description * fix import for firefox * max width for globals * symbols aligned * increase abstraction removed numberRange from textArea Reduced save frequency of solutionTemplate * manage questions give warning * UI change for deployment tab
1 parent 4f5c495 commit d22710a

File tree

5 files changed

+172
-26
lines changed

5 files changed

+172
-26
lines changed

src/components/incubator/EditingWorkspace.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ class AssessmentWorkspace extends React.Component<AssessmentWorkspaceProps, ISta
393393
body: (
394394
<ManageQuestionTab
395395
assessment={assessment}
396+
hasUnsavedChanges={this.state.hasUnsavedChanges}
396397
questionId={questionId}
397398
updateAssessment={this.updateAndSaveAssessment}
398399
/>

src/components/incubator/assessmentTemplates.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export const mcqTemplate = (): IMCQQuestion => {
9797
id: 2,
9898
library: emptyLibrary(),
9999
type: 'mcq',
100-
solution: 1,
100+
solution: 0,
101101
grader: {
102102
name: 'avenger',
103103
id: 1

src/components/incubator/editingWorkspaceSideContent/DeploymentTab.tsx

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { sourceChapters } from '../../../reducers/states';
77

88
import { ExternalLibraryName, IAssessment, Library } from '../../assessment/assessmentShape';
99
import { controlButton } from '../../commons';
10+
import SideContent from '../../workspace/side-content';
1011
import { emptyLibrary } from '../assessmentTemplates';
1112
import { assignToPath, getValueFromPath } from './';
1213
import TextareaContent from './TextareaContent';
@@ -30,10 +31,14 @@ interface IExternal {
3031
symbols: string[];
3132
}
3233

33-
export class DeploymentTab extends React.Component<IProps, { deploymentEnabled: boolean }> {
34+
export class DeploymentTab extends React.Component<
35+
IProps,
36+
{ activeTab: number; deploymentEnabled: boolean }
37+
> {
3438
public constructor(props: IProps) {
3539
super(props);
3640
this.state = {
41+
activeTab: 0,
3742
deploymentEnabled: false
3843
};
3944
}
@@ -58,39 +63,40 @@ export class DeploymentTab extends React.Component<IProps, { deploymentEnabled:
5863
private deploymentTab = () => {
5964
const deploymentPath = this.props.pathToLibrary;
6065
const deployment = getValueFromPath(deploymentPath, this.props.assessment) as Library;
61-
const deploymentDisp = this.props.isGlobalDeployment ? 'Global Deployment' : 'Local Deployment';
66+
// const deploymentDisp = this.props.isGlobalDeployment ? 'Global Deployment' : 'Local Deployment';
6267
const symbols = deployment.external.symbols.map((symbol, i) => (
6368
<tr key={i}>
64-
<td>{this.textareaContent(deploymentPath.concat(['external', 'symbols', i]))}</td>
69+
<td style={{ width: '520px' }}>
70+
{this.textareaContent(deploymentPath.concat(['external', 'symbols', i]))}
71+
</td>
6572
<td>{controlButton('Delete', IconNames.MINUS, this.handleSymbolDelete(i))}</td>
6673
</tr>
6774
));
75+
6876
const globals = deployment.globals.map((symbol, i) => (
6977
<tr key={i}>
70-
<td className="col-xs-3">
71-
{this.textareaContent(deploymentPath.concat(['globals', i, 0]))}
78+
<td className="col-xs-3" style={{ height: '2rem', width: '10rem', overflow: 'auto' }}>
79+
<div style={{ height: '2rem', width: '10rem', overflow: 'auto' }}>
80+
{this.textareaContent(deploymentPath.concat(['globals', i, 0]))}
81+
</div>
82+
</td>
83+
<td className="col-xs-7" style={{ height: '2rem', width: '20rem', overflow: 'auto' }}>
84+
<div style={{ height: '2rem', width: '20rem', overflow: 'auto' }}>
85+
{this.globalValueTextareaContent(i)}
86+
</div>
7287
</td>
73-
<td className="col-xs-7">{this.globalValueTextareaContent(i)}</td>
7488
<td className="col-xs-2">
7589
{controlButton('Delete', IconNames.MINUS, this.handleGlobalDelete(i))}
7690
</td>
7791
</tr>
7892
));
7993

80-
const resetLibrary = controlButton('Reload Library', IconNames.REFRESH, () =>
94+
const resetLibrary = controlButton('Refresh Library', IconNames.REFRESH, () =>
8195
this.props.handleRefreshLibrary(deployment)
8296
);
8397

84-
return (
85-
<div>
86-
{deploymentDisp} {resetLibrary}
87-
<br />
88-
<br />
89-
Interpreter:
90-
<br />
91-
{chapterSelect(deployment.chapter, this.handleChapterSelect)}
92-
<br />
93-
<br />
98+
const symbolsFragment = (
99+
<React.Fragment>
94100
External Library:
95101
<br />
96102
{externalSelect(deployment.external.name, this.handleExternalSelect!)}
@@ -100,16 +106,55 @@ export class DeploymentTab extends React.Component<IProps, { deploymentEnabled:
100106
<br />
101107
<table style={{ width: '100%' }}>{symbols}</table>
102108
{controlButton('New Symbol', IconNames.PLUS, this.handleNewSymbol)}
103-
<br />
104-
<br />
109+
</React.Fragment>
110+
);
111+
112+
const globalsFragment = (
113+
<React.Fragment>
105114
<div>Globals:</div>
106115
<br />
107116
<table style={{ width: '100%' }}>{globals}</table>
108117
{controlButton('New Global', IconNames.PLUS, this.handleNewGlobal)}
118+
</React.Fragment>
119+
);
120+
121+
const tabs = [
122+
{
123+
label: `Library`,
124+
icon: IconNames.BOOK,
125+
body: symbolsFragment
126+
},
127+
{
128+
label: `Globals`,
129+
icon: IconNames.GLOBE,
130+
body: globalsFragment
131+
}
132+
];
133+
134+
return (
135+
<div>
136+
{/* {deploymentDisp}
137+
<br /> */}
138+
{resetLibrary}
139+
<br />
140+
Interpreter:
141+
<br />
142+
{chapterSelect(deployment.chapter, this.handleChapterSelect)}
143+
<SideContent
144+
activeTab={this.state.activeTab}
145+
handleChangeActiveTab={this.handleChangeActiveTab}
146+
tabs={tabs}
147+
/>
109148
</div>
110149
);
111150
};
112151

152+
private handleChangeActiveTab = (tab: number) => {
153+
this.setState({
154+
activeTab: tab
155+
});
156+
};
157+
113158
private textareaContent = (path: Array<string | number>) => {
114159
return (
115160
<TextareaContent

src/components/incubator/editingWorkspaceSideContent/ManageQuestionTab.tsx

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,60 @@
1+
import { ButtonGroup, Classes, Dialog, Intent } from '@blueprintjs/core';
12
import { IconNames } from '@blueprintjs/icons';
23
import * as React from 'react';
34

45
import { IAssessment } from '../../assessment/assessmentShape';
56
import { controlButton } from '../../commons';
7+
import Markdown from '../../commons/Markdown';
68
import { mcqTemplate, programmingTemplate } from '../../incubator/assessmentTemplates';
79

810
interface IProps {
911
assessment: IAssessment;
12+
hasUnsavedChanges: boolean;
1013
questionId: number;
1114
updateAssessment: (assessment: IAssessment) => void;
1215
}
1316

14-
export class ManageQuestionTab extends React.Component<IProps, {}> {
17+
interface IState {
18+
showSaveOverlay: boolean;
19+
modifyAssessment: () => void;
20+
}
21+
22+
export class ManageQuestionTab extends React.Component<IProps, IState> {
1523
public constructor(props: IProps) {
1624
super(props);
25+
this.state = {
26+
showSaveOverlay: false,
27+
modifyAssessment: () => {}
28+
};
1729
}
1830

1931
public render() {
20-
return this.manageQuestionTab();
32+
return (
33+
<div>
34+
{this.confirmSaveOverlay()}
35+
{this.manageQuestionTab()}
36+
</div>
37+
);
2138
}
2239

2340
private manageQuestionTab = () => {
2441
return (
2542
<div>
2643
{controlButton(
27-
'Make Programming Question',
44+
'Insert Programming Question',
2845
IconNames.FONT,
29-
this.makeQuestion(programmingTemplate)
46+
this.confirmSave(this.makeQuestion(programmingTemplate))
47+
)}
48+
{controlButton(
49+
'Insert MCQ Question',
50+
IconNames.CONFIRM,
51+
this.confirmSave(this.makeQuestion(mcqTemplate))
52+
)}
53+
{controlButton(
54+
'Delete Current Question',
55+
IconNames.REMOVE,
56+
this.confirmSave(this.deleteQn)
3057
)}
31-
{controlButton('Make MCQ Question', IconNames.CONFIRM, this.makeQuestion(mcqTemplate))}
32-
{controlButton('Delete Question', IconNames.REMOVE, this.deleteQn)}
3358
</div>
3459
);
3560
};
@@ -56,6 +81,52 @@ export class ManageQuestionTab extends React.Component<IProps, {}> {
5681
assessment.questions = questions;
5782
this.props.updateAssessment(assessment);
5883
};
84+
85+
private confirmSave = (modifyAssessment: () => void) => () => {
86+
if (this.props.hasUnsavedChanges) {
87+
this.setState({
88+
showSaveOverlay: true,
89+
modifyAssessment
90+
});
91+
} else {
92+
modifyAssessment();
93+
}
94+
};
95+
96+
/**
97+
* Asks to save work.
98+
*/
99+
private confirmSaveOverlay = () => (
100+
<Dialog
101+
className="assessment-reset"
102+
icon={IconNames.ERROR}
103+
isCloseButtonShown={false}
104+
isOpen={this.state.showSaveOverlay}
105+
title="Confirmation: Save unsaved changes?"
106+
>
107+
<div className={Classes.DIALOG_BODY}>
108+
<Markdown content="Are you sure you want to save over your unsaved changes?" />
109+
</div>
110+
<div className={Classes.DIALOG_FOOTER}>
111+
<ButtonGroup>
112+
{controlButton('Cancel', null, () => this.setState({ showSaveOverlay: false }), {
113+
minimal: false
114+
})}
115+
{controlButton(
116+
'Confirm',
117+
null,
118+
() => {
119+
this.state.modifyAssessment();
120+
this.setState({
121+
showSaveOverlay: false
122+
});
123+
},
124+
{ minimal: false, intent: Intent.DANGER }
125+
)}
126+
</ButtonGroup>
127+
</div>
128+
</Dialog>
129+
);
59130
}
60131

61132
export default ManageQuestionTab;

src/components/incubator/editingWorkspaceSideContent/QuestionTemplateTab.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { Card } from '@blueprintjs/core';
2+
import { IconNames } from '@blueprintjs/icons';
23
import * as React from 'react';
34
import AceEditor from 'react-ace';
45

56
import { IAssessment, IMCQQuestion } from '../../assessment/assessmentShape';
7+
import { controlButton } from '../../commons';
68
import { assignToPath, getValueFromPath, limitNumberRange } from './';
79
import TextareaContent from './TextareaContent';
810

@@ -117,12 +119,39 @@ export class QuestionTemplateTab extends React.Component<IProps, IState> {
117119
{mcqButton}
118120
Solution:
119121
{this.textareaContent(['questions', questionId, 'solution'], true, [0, 3])}
122+
{controlButton('Add Option', IconNames.CONFIRM, this.addOption)}
123+
{controlButton('Delete Option', IconNames.REMOVE, this.delOption)}
120124
</div>
121125
</Card>
122126
</div>
123127
);
124128
};
125129

130+
private addOption = () => {
131+
const assessment = this.props.assessment;
132+
const questionId = this.props.questionId;
133+
const question = assessment!.questions[questionId] as IMCQQuestion;
134+
const choices = question.choices.concat([
135+
{
136+
content: 'A',
137+
hint: null
138+
}
139+
]);
140+
question.choices = choices;
141+
assessment!.questions[questionId] = question;
142+
this.props.updateAssessment(assessment);
143+
};
144+
145+
private delOption = () => {
146+
const assessment = this.props.assessment;
147+
const questionId = this.props.questionId;
148+
const question = assessment!.questions[questionId] as IMCQQuestion;
149+
const choices = question.choices.slice(0, question.choices.length - 1);
150+
question.choices = choices;
151+
assessment!.questions[questionId] = question;
152+
this.props.updateAssessment(assessment);
153+
};
154+
126155
private textareaContent = (
127156
path: Array<string | number>,
128157
isNumber: boolean = false,

0 commit comments

Comments
 (0)