-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(QuestionBank): Add authoring support (#912)
- Loading branch information
1 parent
ebb7f87
commit fed314c
Showing
17 changed files
with
537 additions
and
140 deletions.
There are no files selected for viewing
101 changes: 101 additions & 0 deletions
101
src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
<div class="question-rules notice-bg-bg"> | ||
<h5 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="4px"> | ||
<span i18n>Question Bank Rules</span> | ||
<button mat-icon-button | ||
color="primary" | ||
matTooltip="Add a new rule" | ||
i18n-matTooltip | ||
matTooltipPosition="above" | ||
(click)="addNewRule(0)"> | ||
<mat-icon>add_circle</mat-icon> | ||
</button> | ||
<span fxFlex></span> | ||
<button mat-button (click)="showHelp()" i18n>Help</button> | ||
</h5> | ||
<ul cdkDropList [cdkDropListData]="feedbackRules" (cdkDropListDropped)="drop($event)" cdkScrollable> | ||
<ng-container *ngFor="let rule of feedbackRules; let ruleIndex = index; let first = first; let last = last"> | ||
<li cdkDrag> | ||
<mat-card class="rule" fxLayout="row wrap" fxLayoutGap="8px"> | ||
<div class="text-secondary" fxLayout="column" fxLayoutAlign="start center" fxLayoutGap="22px"> | ||
<span class="mat-subheading-2">{{ruleIndex + 1}}</span> | ||
<mat-icon cdkDragHandle title="Drag to reorder" i18n-title>drag_indicator</mat-icon> | ||
</div> | ||
<div fxLayout="column" fxLayoutAlign="center start" fxLayoutGap="8px" fxFlex> | ||
<mat-form-field class="rule-input form-field-no-hint" appearance="fill"> | ||
<mat-label i18n>Expression</mat-label> | ||
<input matInput [(ngModel)]="rule.expression" (ngModelChange)='inputChanged.next($event)' /> | ||
</mat-form-field> | ||
<div *ngFor="let question of rule.questions; let questionIndex = index; let last = last; trackBy: customTrackBy" | ||
class="question-item" | ||
fxLayout="row" | ||
fxLayoutAlign="start center" | ||
fxLayoutGap="8px"> | ||
<mat-form-field class="rule-input form-field-no-hint question-input" appearance="fill"> | ||
<mat-label *ngIf="rule.questions.length === 1" i18n>Question</mat-label> | ||
<mat-label *ngIf="rule.questions.length > 1" i18n>Question #{{questionIndex + 1}}</mat-label> | ||
<textarea matInput | ||
[(ngModel)]="rule.questions[questionIndex]" | ||
(ngModelChange)='inputChanged.next($event)' | ||
cdkTextareaAutosize> | ||
</textarea> | ||
<button mat-icon-button | ||
matSuffix | ||
*ngIf="rule.questions.length > 1" | ||
matTooltip="Delete question" | ||
i18n-matTooltip | ||
matTooltipPosition="before" | ||
(click)="deleteFeedbackInRule(rule, questionIndex)"> | ||
<mat-icon>clear</mat-icon> | ||
</button> | ||
</mat-form-field> | ||
<button *ngIf="last" | ||
mat-icon-button | ||
matTooltip="Add new question" | ||
matTooltipPosition="above" | ||
i18n-matTooltip | ||
(click)="addNewFeedbackToRule(rule)" | ||
i18n> | ||
<mat-icon>add_circle</mat-icon> | ||
</button> | ||
</div> | ||
</div> | ||
<div fxLayout="column" fxLayoutAlign="start center"> | ||
<button mat-icon-button | ||
i18n-matTooltip | ||
matTooltip="Delete rule" | ||
matTooltipPosition="before" | ||
(click)="deleteRule(ruleIndex)"> | ||
<mat-icon>clear</mat-icon> | ||
</button> | ||
<button *ngIf="feedbackRules.length > 1" | ||
[disabled]="first" | ||
mat-icon-button | ||
i18n-matTooltip | ||
matTooltip="Move up" | ||
matTooltipPosition="before" | ||
(click)="moveUp(ruleIndex)"> | ||
<mat-icon>arrow_upward</mat-icon> | ||
</button> | ||
<button [disabled]="last" | ||
mat-icon-button | ||
i18n-matTooltip | ||
matTooltip="Move down" | ||
matTooltipPosition="before" | ||
(click)="moveDown(ruleIndex)"> | ||
<mat-icon>arrow_downward</mat-icon> | ||
</button> | ||
</div> | ||
</mat-card> | ||
</li> | ||
</ng-container> | ||
</ul> | ||
<button *ngIf="feedbackRules.length > 0" | ||
mat-icon-button | ||
color="primary" | ||
matTooltip="Add a new rule at the end" | ||
i18n-matTooltip | ||
matTooltipPosition="above" | ||
(click)="addNewRule(feedbackRules.length)"> | ||
<mat-icon>add_circle</mat-icon> | ||
</button> | ||
</div> |
50 changes: 50 additions & 0 deletions
50
src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
@import '~style/abstracts/variables'; | ||
|
||
.question-rules { | ||
padding: 16px; | ||
border-radius: $card-border-radius; | ||
} | ||
|
||
h5 { | ||
margin-top: 0; | ||
} | ||
|
||
ul { | ||
padding: 0; | ||
max-height: 60vh; | ||
overflow-y: auto; | ||
} | ||
|
||
li { | ||
list-style-type: none; | ||
} | ||
|
||
.rule { | ||
width: 100%; | ||
padding: 8px; | ||
margin-bottom: 8px; | ||
} | ||
|
||
.rule-input { | ||
width: 100%; | ||
} | ||
|
||
.cdk-drag-handle { | ||
cursor: move; | ||
} | ||
|
||
.cdk-drag-placeholder { | ||
opacity: .4; | ||
} | ||
|
||
.mat-subheading-2 { | ||
margin: 0; | ||
} | ||
|
||
.question-item { | ||
width: 100%; | ||
} | ||
|
||
.question-input { | ||
width: 100%; | ||
} |
66 changes: 66 additions & 0 deletions
66
src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { DragDropModule } from '@angular/cdk/drag-drop'; | ||
import { MatDialogModule } from '@angular/material/dialog'; | ||
import { MatIconModule } from '@angular/material/icon'; | ||
import { HttpClientTestingModule } from '@angular/common/http/testing'; | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
import { QuestionBankRule } from '../../../assets/wise5/components/peerChat/peer-chat-question-bank/QuestionBankRule'; | ||
import { TeacherProjectService } from '../../../assets/wise5/services/teacherProjectService'; | ||
import { StudentTeacherCommonServicesModule } from '../../student-teacher-common-services.module'; | ||
import { EditQuestionBankRulesComponent } from './edit-question-bank-rules.component'; | ||
|
||
let component: EditQuestionBankRulesComponent; | ||
let fixture: ComponentFixture<EditQuestionBankRulesComponent>; | ||
let projectService: TeacherProjectService; | ||
let nodeChangedSpy; | ||
|
||
describe('EditQuestionBankRulesComponent', () => { | ||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
declarations: [EditQuestionBankRulesComponent], | ||
imports: [ | ||
DragDropModule, | ||
HttpClientTestingModule, | ||
MatDialogModule, | ||
MatIconModule, | ||
StudentTeacherCommonServicesModule | ||
], | ||
providers: [TeacherProjectService] | ||
}).compileComponents(); | ||
projectService = TestBed.inject(TeacherProjectService); | ||
nodeChangedSpy = spyOn(projectService, 'nodeChanged'); | ||
}); | ||
|
||
beforeEach(() => { | ||
fixture = TestBed.createComponent(EditQuestionBankRulesComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
addNewFeedbackToRule(); | ||
deleteFeedbackInRule(); | ||
}); | ||
|
||
function addNewFeedbackToRule() { | ||
describe('addNewFeedbackToRule()', () => { | ||
it('should add new question to rule', () => { | ||
const rule = new QuestionBankRule({ questions: ['Q1'] }); | ||
component.addNewFeedbackToRule(rule); | ||
expect(nodeChangedSpy).toHaveBeenCalled(); | ||
expect(rule.questions.length).toEqual(2); | ||
expect(rule.questions[1]).toEqual(''); | ||
}); | ||
}); | ||
} | ||
|
||
function deleteFeedbackInRule() { | ||
describe('deleteFeedbackInRule()', () => { | ||
it('should delete specified feedback', () => { | ||
const rule = new QuestionBankRule({ questions: ['Q1', 'Q2'] }); | ||
spyOn(window, 'confirm').and.returnValue(true); | ||
component.deleteFeedbackInRule(rule, 0); | ||
expect(nodeChangedSpy).toHaveBeenCalled(); | ||
expect(rule.questions.length).toEqual(1); | ||
expect(rule.questions[0]).toEqual('Q2'); | ||
}); | ||
}); | ||
} |
48 changes: 48 additions & 0 deletions
48
src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { Component } from '@angular/core'; | ||
import { MatDialog } from '@angular/material/dialog'; | ||
import { EditFeedbackRulesComponent } from '../../../assets/wise5/components/common/feedbackRule/edit-feedback-rules/edit-feedback-rules.component'; | ||
import { QuestionBankRule } from '../../../assets/wise5/components/peerChat/peer-chat-question-bank/QuestionBankRule'; | ||
import { RandomKeyService } from '../../../assets/wise5/services/randomKeyService'; | ||
import { TeacherProjectService } from '../../../assets/wise5/services/teacherProjectService'; | ||
|
||
@Component({ | ||
selector: 'edit-question-bank-rules', | ||
templateUrl: './edit-question-bank-rules.component.html', | ||
styleUrls: ['./edit-question-bank-rules.component.scss'] | ||
}) | ||
export class EditQuestionBankRulesComponent extends EditFeedbackRulesComponent { | ||
constructor(protected dialog: MatDialog, protected projectService: TeacherProjectService) { | ||
super(dialog, projectService); | ||
} | ||
|
||
ngOnInit(): void { | ||
super.ngOnInit(); | ||
} | ||
|
||
protected createNewFeedbackRule(): Partial<QuestionBankRule> { | ||
return { id: RandomKeyService.generate(), expression: '', questions: [''] }; | ||
} | ||
|
||
deleteRule(ruleIndex: number): void { | ||
if (confirm($localize`Are you sure you want to delete this question rule?`)) { | ||
this.feedbackRules.splice(ruleIndex, 1); | ||
this.projectService.nodeChanged(); | ||
} | ||
} | ||
|
||
addNewFeedbackToRule(rule: Partial<QuestionBankRule>): void { | ||
(rule.questions as string[]).push(''); | ||
this.projectService.nodeChanged(); | ||
} | ||
|
||
deleteFeedbackInRule(rule: QuestionBankRule, feedbackIndex: number): void { | ||
if (confirm($localize`Are you sure you want to delete this question?`)) { | ||
(rule.questions as string[]).splice(feedbackIndex, 1); | ||
this.projectService.nodeChanged(); | ||
} | ||
} | ||
|
||
customTrackBy(index: number): number { | ||
return index; | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
src/app/authoring-tool/edit-question-bank/edit-question-bank.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<div class="section-container"> | ||
<div class="enable-question-bank-div"> | ||
<mat-checkbox | ||
[checked]="componentContent.questionBank?.enabled" | ||
(change)="toggleComponent($event)" | ||
color="primary" | ||
i18n> | ||
Enable Question Bank | ||
</mat-checkbox> | ||
</div> | ||
<div *ngIf="componentContent.questionBank?.enabled"> | ||
<div class="reference-component-div" | ||
fxLayout="row wrap" | ||
fxLayoutAlign="start center" | ||
fxLayoutGap="20px"> | ||
<mat-label class="bold" i18n>Reference Component:</mat-label> | ||
<edit-connected-component-node-select | ||
[connectedComponent]="componentContent.questionBank.referenceComponent" | ||
(connectedComponentChange)="referenceComponentNodeIdChanged($event)"> | ||
</edit-connected-component-node-select> | ||
<edit-connected-component-component-select | ||
[componentId]="componentId" | ||
[connectedComponent]="componentContent.questionBank.referenceComponent" | ||
[allowedConnectedComponentTypes]="allowedReferenceComponentTypes" | ||
(connectedComponentChange)="saveChanges()"> | ||
</edit-connected-component-component-select> | ||
<edit-component-peer-grouping-tag | ||
[componentContent]="componentContent.questionBank"> | ||
</edit-component-peer-grouping-tag> | ||
</div> | ||
<div class="feedback-rules-div"> | ||
<edit-question-bank-rules [feedbackRules]="componentContent.questionBank.rules"> | ||
</edit-question-bank-rules> | ||
</div> | ||
</div> | ||
</div> |
21 changes: 21 additions & 0 deletions
21
src/app/authoring-tool/edit-question-bank/edit-question-bank.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
@import '~style/abstracts/variables', '~style/themes/default'; | ||
|
||
.prompt { | ||
width: 100%; | ||
} | ||
|
||
.section-container { | ||
padding: 16px; | ||
border: 2px solid #dddddd; | ||
border-radius: $card-border-radius; | ||
margin-top: 20px; | ||
margin-bottom: 20px; | ||
} | ||
|
||
.reference-component-div { | ||
margin-top: 10px; | ||
} | ||
|
||
.feedback-rules-div { | ||
margin-bottom: 20px; | ||
} |
40 changes: 40 additions & 0 deletions
40
src/app/authoring-tool/edit-question-bank/edit-question-bank.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { HttpClientTestingModule } from '@angular/common/http/testing'; | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
import { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox'; | ||
import { TeacherProjectService } from '../../../assets/wise5/services/teacherProjectService'; | ||
import { StudentTeacherCommonServicesModule } from '../../student-teacher-common-services.module'; | ||
import { EditQuestionBankComponent } from './edit-question-bank.component'; | ||
|
||
let component: EditQuestionBankComponent; | ||
let fixture: ComponentFixture<EditQuestionBankComponent>; | ||
|
||
describe('EditQuestionBankComponent', () => { | ||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
declarations: [EditQuestionBankComponent], | ||
imports: [HttpClientTestingModule, MatCheckboxModule, StudentTeacherCommonServicesModule], | ||
providers: [TeacherProjectService] | ||
}).compileComponents(); | ||
}); | ||
beforeEach(() => { | ||
fixture = TestBed.createComponent(EditQuestionBankComponent); | ||
component = fixture.componentInstance; | ||
component.componentContent = {}; | ||
fixture.detectChanges(); | ||
}); | ||
toggleComponent(); | ||
}); | ||
|
||
function toggleComponent() { | ||
describe('toggleComponent()', () => { | ||
it('should toggle component on when it does not exist', () => { | ||
expect(component.componentContent.questionBank).toBeUndefined(); | ||
component.toggleComponent({ checked: true } as MatCheckboxChange); | ||
expect(component.componentContent.questionBank.enabled).toBeTrue(); | ||
}); | ||
it('should toggle component off', () => { | ||
component.toggleComponent({ checked: false } as MatCheckboxChange); | ||
expect(component.componentContent.questionBank.enabled).toBeFalse(); | ||
}); | ||
}); | ||
} |
Oops, something went wrong.