Skip to content

Commit

Permalink
feat(Question Bank): Click question to add to student response input (#…
Browse files Browse the repository at this point in the history
…1416)

Co-authored-by: Jonathan Lim-Breitbart <breity10@gmail.com>
  • Loading branch information
geoffreykwan and breity authored Sep 18, 2023
1 parent 16d6b90 commit 21b1055
Show file tree
Hide file tree
Showing 27 changed files with 407 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,25 @@ <h5 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="4px">
<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>
<ng-container *ngIf="version === 2; then version2; else version1"></ng-container>
<ng-template #version2>
<textarea
matInput
[(ngModel)]="rule.questions[questionIndex].text"
(ngModelChange)="inputChanged.next($event)"
cdkTextareaAutosize
>
</textarea>
</ng-template>
<ng-template #version1>
<textarea
matInput
[(ngModel)]="rule.questions[questionIndex]"
(ngModelChange)="inputChanged.next($event)"
cdkTextareaAutosize
>
</textarea>
</ng-template>
<button
mat-icon-button
matSuffix
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import { QuestionBankRule } from '../../../assets/wise5/components/peerChat/peer
import { TeacherProjectService } from '../../../assets/wise5/services/teacherProjectService';
import { StudentTeacherCommonServicesModule } from '../../student-teacher-common-services.module';
import { EditQuestionBankRulesComponent } from './edit-question-bank-rules.component';
import { Question } from '../../../assets/wise5/components/peerChat/peer-chat-question-bank/Question';

let component: EditQuestionBankRulesComponent;
let fixture: ComponentFixture<EditQuestionBankRulesComponent>;
let projectService: TeacherProjectService;
let nodeChangedSpy;
let nodeChangedSpy: jasmine.Spy;

describe('EditQuestionBankRulesComponent', () => {
beforeEach(async () => {
Expand Down Expand Up @@ -42,19 +43,42 @@ describe('EditQuestionBankRulesComponent', () => {

function addNewFeedbackToRule() {
describe('addNewFeedbackToRule()', () => {
it('should add new question to rule', () => {
const rule = new QuestionBankRule({ questions: ['Q1'] });
addNewFeedbackToRuleVersion1();
addNewFeedbackToRuleVersion2();
});
}

function addNewFeedbackToRuleVersion1() {
describe('using question bank content version 1', () => {
it('adds new question to rule', () => {
component.version = undefined;
const rule = new QuestionBankRule({ questions: [] });
component.addNewFeedbackToRule(rule);
expect(nodeChangedSpy).toHaveBeenCalled();
expect(rule.questions.length).toEqual(2);
expect(rule.questions[1]).toEqual('');
expect(rule.questions.length).toEqual(1);
expect(rule.questions[0]).toEqual('');
});
});
}

function addNewFeedbackToRuleVersion2() {
describe('using question bank content version 2', () => {
it('adds new question to rule', () => {
component.version = 2;
const rule = new QuestionBankRule({ questions: [] });
component.addNewFeedbackToRule(rule);
expect(nodeChangedSpy).toHaveBeenCalled();
expect(rule.questions.length).toEqual(1);
const question = rule.questions[0] as Question;
expect(question.hasOwnProperty('id')).toEqual(true);
expect(question.text).toEqual('');
});
});
}

function deleteFeedbackInRule() {
describe('deleteFeedbackInRule()', () => {
it('should delete specified feedback', () => {
it('deletes specified feedback', () => {
const rule = new QuestionBankRule({ questions: ['Q1', 'Q2'] });
spyOn(window, 'confirm').and.returnValue(true);
component.deleteFeedbackInRule(rule, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { generateRandomKey } from '../../../assets/wise5/common/string/string';
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 { TeacherProjectService } from '../../../assets/wise5/services/teacherProjectService';
import { Question } from '../../../assets/wise5/components/peerChat/peer-chat-question-bank/Question';

@Component({
selector: 'edit-question-bank-rules',
Expand All @@ -20,7 +21,11 @@ export class EditQuestionBankRulesComponent extends EditFeedbackRulesComponent {
}

protected createNewFeedbackRule(): Partial<QuestionBankRule> {
return { id: generateRandomKey(), expression: '', questions: [''] };
if (this.version === 2) {
return { id: generateRandomKey(), expression: '', questions: [new Question()] };
} else {
return { id: generateRandomKey(), expression: '', questions: [''] };
}
}

deleteRule(ruleIndex: number): void {
Expand All @@ -31,7 +36,11 @@ export class EditQuestionBankRulesComponent extends EditFeedbackRulesComponent {
}

addNewFeedbackToRule(rule: Partial<QuestionBankRule>): void {
(rule.questions as string[]).push('');
if (this.version === 2) {
(rule.questions as any[]).push(new Question());
} else {
(rule.questions as string[]).push('');
}
this.projectService.nodeChanged();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,36 @@
</mat-checkbox>
</div>
<div *ngIf="componentContent.questionBank?.enabled">
<div class="custom-label">
<mat-form-field appearance="fill">
<mat-label i18n>Custom Label</mat-label>
<input
matInput
[(ngModel)]="componentContent.questionBank.label"
(ngModelChange)="inputChanged.next($event)"
placeholder="Question Bank"
i18n-placeholder
/>
</mat-form-field>
</div>
<div *ngIf="componentContent.questionBank.version === 2">
<mat-checkbox
[(ngModel)]="componentContent.questionBank.clickToUseEnabled"
(ngModelChange)="saveChanges()"
color="primary"
i18n
>
Student can select questions to use in Peer Chat
</mat-checkbox>
</div>
<div class="max-questions">
<mat-form-field appearance="fill">
<mat-label i18n>Max Number of Questions</mat-label>
<input
matInput
type="number"
[(ngModel)]="componentContent.questionBank.maxQuestionsToShow"
(ngModelChange)="saveChanges()"
(ngModelChange)="inputChanged.next($event)"
/>
</mat-form-field>
</div>
Expand All @@ -40,7 +62,10 @@
</edit-component-peer-grouping-tag>
</div>
<div class="feedback-rules">
<edit-question-bank-rules [feedbackRules]="componentContent.questionBank.rules">
<edit-question-bank-rules
[feedbackRules]="componentContent.questionBank.rules"
[version]="componentContent.questionBank.version"
>
</edit-question-bank-rules>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
margin-bottom: 16px;
}

.reference-component, .max-questions {
.custom-label, .reference-component, .max-questions {
margin-top: 16px;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,39 @@ import { Component, Input, OnInit } from '@angular/core';
import { MatCheckboxChange } from '@angular/material/checkbox';
import { QuestionBank } from '../../../assets/wise5/components/peerChat/peer-chat-question-bank/QuestionBank';
import { TeacherProjectService } from '../../../assets/wise5/services/teacherProjectService';
import { Subject, Subscription, debounceTime, distinctUntilChanged } from 'rxjs';

@Component({
selector: 'edit-question-bank',
templateUrl: './edit-question-bank.component.html',
styleUrls: ['./edit-question-bank.component.scss']
})
export class EditQuestionBankComponent implements OnInit {
allowedReferenceComponentTypes: string[] = ['MultipleChoice', 'OpenResponse'];
protected allowedReferenceComponentTypes: string[] = ['MultipleChoice', 'OpenResponse'];
@Input() componentContent: any;
protected inputChanged: Subject<string> = new Subject<string>();
private subscriptions: Subscription = new Subscription();

constructor(private projectService: TeacherProjectService) {}

ngOnInit(): void {}
ngOnInit(): void {
this.subscriptions.add(
this.inputChanged.pipe(debounceTime(1000), distinctUntilChanged()).subscribe(() => {
this.projectService.nodeChanged();
})
);
}

ngOnDestroy(): void {
this.subscriptions.unsubscribe();
}

toggleComponent(event: MatCheckboxChange): void {
if (this.componentContent.questionBank == null) {
this.componentContent.questionBank = new QuestionBank({
referenceComponent: {},
rules: []
rules: [],
version: 2
});
}
this.componentContent.questionBank.enabled = event.checked;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
></peer-chat-messages>
<peer-chat-message-input
*ngIf="isEnabled"
(onSubmit)="submit.emit($event)"
[messageText]="response"
(onSubmit)="submitResponse($event)"
(responseChangedEvent)="responseChanged($event)"
></peer-chat-message-input>
</mat-card>
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,17 @@ import { PeerChatMessage } from '../PeerChatMessage';
styleUrls: ['./peer-chat-chat-box.component.scss']
})
export class PeerChatChatBoxComponent implements OnInit {
@Input()
isEnabled: boolean = true;

@Input()
isGrading: boolean = false;

@Input()
messages: PeerChatMessage[] = [];

@Input()
myWorkgroupId: number;

@Input()
workgroupInfos: any = {};

@Input() isEnabled: boolean = true;
@Input() isGrading: boolean = false;
@Input() messages: PeerChatMessage[] = [];
@Input() myWorkgroupId: number;
@Input() response: string = '';
@Input() workgroupInfos: any = {};
workgroupInfosWithoutTeachers: any[];

@Output()
deleteClickedEvent: EventEmitter<PeerChatMessage> = new EventEmitter<PeerChatMessage>();

@Output('onSubmit')
submit: EventEmitter<string> = new EventEmitter<string>();

@Output() deleteClickedEvent: EventEmitter<PeerChatMessage> = new EventEmitter<PeerChatMessage>();
@Output() responseChangedEvent: EventEmitter<string> = new EventEmitter<string>();
@Output('onSubmit') submit: EventEmitter<string> = new EventEmitter<string>();
@Output()
undeleteClickedEvent: EventEmitter<PeerChatMessage> = new EventEmitter<PeerChatMessage>();

Expand All @@ -48,4 +36,13 @@ export class PeerChatChatBoxComponent implements OnInit {
protected undeleteClicked(peerChatMessage: PeerChatMessage): void {
this.undeleteClickedEvent.emit(peerChatMessage);
}

protected submitResponse(event: string): void {
this.submit.emit(event);
this.response = '';
}

protected responseChanged(response: string): void {
this.responseChangedEvent.emit(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
>
<peer-chat-question-bank
*ngIf="componentContent.questionBank?.enabled"
[content]="componentContent"
[displayedQuestionBankRules]="questionBankRules"
[questionIdsUsed]="questionIdsUsed"
></peer-chat-question-bank>
</div>
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
cdkTextareaAutosize
placeholder="Add response..."
i18n-placeholder
(focus)="onFocus($event)"
[(ngModel)]="messageText"
(ngModelChange)="responseChanged()"
(keypress)="keyPressed($event)"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';

@Component({
selector: 'peer-chat-message-input',
templateUrl: './peer-chat-message-input.component.html'
})
export class PeerChatMessageInputComponent implements OnInit {
@Output('onSubmit')
submit: EventEmitter<string> = new EventEmitter<string>();

isSubmitEnabled: boolean = false;
messageText: string;
protected isSubmitEnabled: boolean = false;
@Input() messageText: string = '';
@Output() responseChangedEvent: EventEmitter<string> = new EventEmitter<string>();
@Output('onSubmit') submit: EventEmitter<string> = new EventEmitter<string>();

ngOnInit(): void {}

responseChanged(): void {
this.isSubmitEnabled = this.messageText.length > 0;
ngOnChanges(): void {
this.responseChanged();
}

protected responseChanged(): void {
this.isSubmitEnabled = this.messageText?.length > 0;
this.responseChangedEvent.emit(this.messageText);
}

keyPressed(event: any): void {
protected keyPressed(event: any): void {
if (event.keyCode === 13) {
event.preventDefault();
if (this.isSubmitEnabled) {
Expand All @@ -26,9 +30,19 @@ export class PeerChatMessageInputComponent implements OnInit {
}
}

submitResponse(): void {
protected submitResponse(): void {
this.submit.emit(this.messageText);
this.messageText = '';
this.isSubmitEnabled = false;
}

protected onFocus(event: any): void {
this.placeCursorAtEndOfMessageText(event);
}

private placeCursorAtEndOfMessageText(event: any): void {
const messageLength = this.messageText.length;
event.srcElement.selectionStart = messageLength;
event.srcElement.selectionEnd = messageLength;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { generateRandomKey } from '../../../common/string/string';

export class Question {
id: string;
text: string = '';

constructor() {
this.id = generateRandomKey();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { ReferenceComponentRules } from '../../common/ReferenceComponentRules';
import { QuestionBankRule } from './QuestionBankRule';

export class QuestionBank extends ReferenceComponentRules {
clickToUseEnabled: boolean;
label: string;
maxQuestionsToShow: number;
rules: QuestionBankRule[];
version: number;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FeedbackRule } from '../../common/feedbackRule/FeedbackRule';
import { Question } from './Question';

export class QuestionBankRule extends FeedbackRule {
questions: string[];
questions: (string | Question)[];
}
Loading

0 comments on commit 21b1055

Please sign in to comment.