Skip to content

Commit ed6ddc1

Browse files
Mutugiiidogi
andauthored
teams: smoother survey rating scaling (fixes #9906) (#9907)
Co-authored-by: dogi <dogi@users.noreply.github.com>
1 parent 4174624 commit ed6ddc1

11 files changed

Lines changed: 352 additions & 133 deletions

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "planet",
33
"license": "AGPL-3.0",
4-
"version": "0.22.89",
4+
"version": "0.22.90",
55
"myplanet": {
66
"latest": "v0.55.91",
77
"min": "v0.53.91"

src/app/exams/exams-add.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
<button mat-menu-item type="button" (click)="addQuestion('textarea')" i18n>Text - Long answer</button>
2626
<button mat-menu-item type="button" (click)="addQuestion('select')" i18n>Multiple Choice - single answer</button>
2727
<button mat-menu-item type="button" (click)="addQuestion('selectMultiple')" i18n>Multiple Choice - multiple answer</button>
28-
<button *ngIf="examType !== 'exam'" mat-menu-item type="button" (click)="addQuestion('ratingScale')" i18n>Rating Scale - 1 to 9</button>
28+
<button *ngIf="examType !== 'exam'" mat-menu-item type="button" (click)="addQuestion('ratingScale')" i18n>Rating Scale</button>
2929
</mat-menu>
3030
<mat-accordion class="exam-inputs" *ngIf="!isCourseContent">
3131
<mat-expansion-panel [expanded]="!isQuestionsActive">

src/app/exams/exams-question.component.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
<form class="vertical-form" [formGroup]="questionForm">
22
<div class="type-title-container">
3+
<div class="scale-max-field" *ngIf="questionForm.controls.type.value === 'ratingScale'">
4+
<span class="scale-max-label" i18n>Scale</span>
5+
<mat-button-toggle-group formControlName="scaleMax">
6+
<mat-button-toggle *ngFor="let n of scaleMaxOptions" [value]="n">{{n}}</mat-button-toggle>
7+
</mat-button-toggle-group>
8+
</div>
39
<mat-form-field>
410
<mat-label i18n>Type</mat-label>
511
<mat-select formControlName="type" (selectionChange)="clearChoices()" panelClass="exam-question-select">
612
<mat-option value="input" i18n>Text - Short answer</mat-option>
713
<mat-option value="textarea" i18n>Text - Long answer</mat-option>
814
<mat-option value="select" i18n>Multiple Choice - single answer</mat-option>
915
<mat-option value="selectMultiple" i18n>Multiple Choice - multiple answer</mat-option>
10-
<mat-option *ngIf="examType !== 'exam'" value="ratingScale" i18n>Rating Scale - 1 to 9</mat-option>
16+
<mat-option *ngIf="examType !== 'exam'" value="ratingScale" i18n>Rating Scale</mat-option>
1117
</mat-select>
1218
</mat-form-field>
1319
</div>

src/app/exams/exams-question.component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { NgIf, NgFor } from '@angular/common';
1616
import { PlanetMarkdownTextboxComponent } from '../shared/forms/planet-markdown-textbox.component';
1717
import { FormErrorMessagesComponent } from '../shared/forms/form-error-messages.component';
1818
import { MatButton, MatIconButton } from '@angular/material/button';
19+
import { MatButtonToggle, MatButtonToggleGroup } from '@angular/material/button-toggle';
1920
import { MatCheckbox } from '@angular/material/checkbox';
2021
import { MatInput } from '@angular/material/input';
2122
import { MatIcon } from '@angular/material/icon';
@@ -27,7 +28,7 @@ import { MatIcon } from '@angular/material/icon';
2728
imports: [
2829
FormsModule, ReactiveFormsModule, MatFormField, MatLabel, MatSelect, MatOption, NgIf,
2930
PlanetMarkdownTextboxComponent, MatError, FormErrorMessagesComponent, MatButton, NgFor, MatCheckbox,
30-
MatInput, MatIconButton, MatSuffix, MatIcon
31+
MatInput, MatIconButton, MatSuffix, MatIcon, MatButtonToggle, MatButtonToggleGroup
3132
]
3233
})
3334
export class ExamsQuestionComponent implements OnInit, OnChanges, OnDestroy, AfterViewChecked {
@@ -38,6 +39,7 @@ export class ExamsQuestionComponent implements OnInit, OnChanges, OnDestroy, Aft
3839
@Output() questionRemove = new EventEmitter<any>();
3940
@ViewChildren('choiceInput') choiceInputs: QueryList<ElementRef>;
4041
correctCheckboxes: any = {};
42+
scaleMaxOptions = [ 5, 6, 7, 8, 9 ];
4143
questionForm: QuestionFormGroup = this.examsService.newQuestionForm(this.examType === 'courses');
4244
initializing = true;
4345
choiceAdded = false;

src/app/exams/exams-question.scss

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
form {
66
display: grid;
77
grid-template-rows: 56px auto 56px;
8+
grid-row-gap: 1rem;
89
}
910

1011
.question-choices {
@@ -20,8 +21,21 @@
2021

2122
.type-title-container {
2223
display: grid;
23-
grid-template-columns: 1fr v.$form-width-1;
24-
grid-column-gap: 0.5rem;
24+
grid-template-columns: 2fr 8fr;
25+
grid-column-gap: 1rem;
26+
align-items: center;
27+
}
28+
29+
.scale-max-field {
30+
display: flex;
31+
align-items: center;
32+
gap: 0.75rem;
33+
flex-wrap: wrap;
34+
}
35+
36+
.scale-max-label {
37+
font-size: 14px;
38+
color: rgba(0, 0, 0, 0.6);
2539
}
2640

2741
}

src/app/exams/exams-view.component.html

Lines changed: 100 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -32,86 +32,114 @@
3232
<button mat-icon-button [disabled]="isLoading || questionNum === 1" (click)="moveQuestion(-1)"><mat-icon>navigate_before</mat-icon></button>
3333
<button mat-icon-button [disabled]="isLoading || questionNum === maxQuestions" (click)="nextQuestion({ nextClicked: true })"><mat-icon>navigate_next</mat-icon></button>
3434
</mat-toolbar>
35+
<div class="progress-bar-track" *ngIf="!isLoading && maxQuestions > 0">
36+
<div class="progress-bar-fill" [style.width.%]="progressPercent"></div>
37+
</div>
3538
<div class="view-container" [ngClass]="{ 'view-full-height': !isDialog }">
3639
<ng-container *ngIf="!isLoading; else LoadingContent">
37-
<td-markdown [content]="question?.body"></td-markdown>
38-
<div [ngSwitch]="mode">
39-
<ng-container *ngSwitchCase="'take'">
40-
<ng-container [ngSwitch]="question?.type">
41-
<mat-form-field class="full-width" *ngSwitchCase="'input'">
42-
<mat-label i18n>Enter answer here</mat-label>
43-
<input matInput [formControl]="answer">
44-
</mat-form-field>
45-
<mat-form-field *ngSwitchCase="'textarea'" class="full-width mat-form-field-type-no-underline">
46-
<planet-markdown-textbox [formControl]="answer"></planet-markdown-textbox>
47-
</mat-form-field>
48-
<mat-radio-group *ngSwitchCase="'select'" class="question-list" [formControl]="answer">
49-
<mat-radio-button [value]="option" *ngFor="let option of question?.choices">
50-
<span class="multiple-choice-text">{{option.text}}</span>
51-
</mat-radio-button>
52-
<ng-container *ngIf="question?.hasOtherOption">
53-
<div class="other-option-flex">
54-
<mat-radio-button [value]="currentOtherOption">
55-
<span class="multiple-choice-text" i18n>Other: </span>
40+
<div [ngClass]="slideDirection === 'right'
41+
? (slideAnimationVariant === 'a' ? 'slide-in-right-a' : 'slide-in-right-b')
42+
: (slideAnimationVariant === 'a' ? 'slide-in-left-a' : 'slide-in-left-b')">
43+
<td-markdown [content]="question?.body"></td-markdown>
44+
<div [ngSwitch]="mode">
45+
<ng-container *ngSwitchCase="'take'">
46+
<ng-container [ngSwitch]="question?.type">
47+
<mat-form-field class="full-width" *ngSwitchCase="'input'">
48+
<mat-label i18n>Enter answer here</mat-label>
49+
<input matInput [formControl]="answer">
50+
</mat-form-field>
51+
<mat-form-field *ngSwitchCase="'textarea'" class="full-width mat-form-field-type-no-underline">
52+
<planet-markdown-textbox [formControl]="answer"></planet-markdown-textbox>
53+
</mat-form-field>
54+
<mat-radio-group *ngSwitchCase="'select'" class="question-list" [formControl]="answer">
55+
<div class="option-card" *ngFor="let option of question?.choices" [class.selected]="isSelectOptionSelected(option)" (click)="selectOption(option)">
56+
<mat-radio-button [value]="option">
57+
<span class="multiple-choice-text">{{option.text}}</span>
5658
</mat-radio-button>
57-
<input matInput
58-
class="other-option-input"
59-
[disabled]="!isOtherSelected()"
60-
[(ngModel)]="currentOtherOption.text"
61-
(ngModelChange)="updateOtherText()">
59+
<mat-icon class="option-check" *ngIf="isSelectOptionSelected(option)">check</mat-icon>
6260
</div>
63-
</ng-container>
64-
</mat-radio-group>
65-
<div *ngSwitchCase="'selectMultiple'" class="question-list">
66-
<span class="mat-caption" i18n>
67-
{examType, select, survey {You can choose one or more answers.} exam {There are one or more correct answers. Please choose all correct answers.}}
68-
</span>
69-
<mat-checkbox *ngFor="let option of question?.choices" [value]="option" (change)="setAnswer($event, option)" [checked]="checkboxState[option.id]">
70-
<span class="multiple-choice-text">{{option.text}}</span>
71-
</mat-checkbox>
72-
<ng-container *ngIf="question?.hasOtherOption">
73-
<div class="other-option-flex">
74-
<mat-checkbox [checked]="checkboxState['other']" (change)="toggleOtherMultiple($event)">
75-
<span class="multiple-choice-text" i18n>Other: </span>
61+
<ng-container *ngIf="question?.hasOtherOption">
62+
<div class="option-card" [class.selected]="isOtherSelected()" (click)="selectOtherRadio()">
63+
<mat-radio-button [value]="currentOtherOption">
64+
<span class="multiple-choice-text" i18n>Other: </span>
65+
</mat-radio-button>
66+
<div class="other-option-flex">
67+
<input matInput
68+
#singleOtherInput
69+
class="other-option-input"
70+
[(ngModel)]="currentOtherOption.text"
71+
(ngModelChange)="updateOtherText()"
72+
(focus)="selectOtherRadio()"
73+
(click)="$event.stopPropagation()">
74+
</div>
75+
<mat-icon class="option-check" *ngIf="isOtherSelected()">check</mat-icon>
76+
</div>
77+
</ng-container>
78+
</mat-radio-group>
79+
<div *ngSwitchCase="'selectMultiple'" class="question-list">
80+
<span i18n>
81+
{examType, select, survey {You can choose one or more answers.} exam {There are one or more correct answers. Please choose all correct answers.}}
82+
</span>
83+
<div class="option-card" *ngFor="let option of question?.choices" [class.selected]="checkboxState[option.id]" (click)="toggleMultipleOption(option)">
84+
<mat-checkbox [value]="option" (change)="setAnswer($event, option)" [checked]="checkboxState[option.id]" (click)="$event.stopPropagation()">
85+
<span class="multiple-choice-text">{{option.text}}</span>
7686
</mat-checkbox>
77-
<input matInput class="other-option-input" [disabled]="!checkboxState['other']" [(ngModel)]="currentOtherOption.text" (ngModelChange)="updateOtherText()">
87+
<mat-icon class="option-check" *ngIf="checkboxState[option.id]">check</mat-icon>
7888
</div>
79-
</ng-container>
80-
</div>
81-
<div *ngSwitchCase="'ratingScale'" class="rating-scale-keypad">
82-
<div class="rating-scale-grid">
83-
<button type="button" mat-raised-button class="rating-scale-button" *ngFor="let num of [1,2,3,4,5,6,7,8,9]" [class.selected]="answer.value === num.toString()" [attr.data-rating]="num" (click)="setRatingScaleAnswer(num)">
84-
{{num}}
85-
</button>
89+
<ng-container *ngIf="question?.hasOtherOption">
90+
<div class="option-card" [class.selected]="checkboxState['other']" (click)="toggleOtherCheckbox()">
91+
<mat-checkbox [checked]="checkboxState['other']" (change)="toggleOtherMultiple($event)" (click)="$event.stopPropagation()">
92+
<span class="multiple-choice-text" i18n>Other: </span>
93+
</mat-checkbox>
94+
<div class="other-option-flex">
95+
<input matInput #multipleOtherInput class="other-option-input" [(ngModel)]="currentOtherOption.text" (ngModelChange)="updateOtherText()" (focus)="ensureOtherCheckboxSelected()" (click)="$event.stopPropagation()">
96+
</div>
97+
<mat-icon class="option-check" *ngIf="checkboxState['other']">check</mat-icon>
98+
</div>
99+
</ng-container>
86100
</div>
87-
</div>
101+
<ng-container *ngSwitchCase="'ratingScale'">
102+
<div class="rating-scale-keypad rating-scale-keypad--grid" *ngIf="(question?.scaleMax ?? 9) === 9; else ratingScaleRow">
103+
<button type="button" mat-raised-button class="rating-scale-button rating-scale-button--phone" *ngFor="let num of ratingScaleNumbers" [class.selected]="answer.value === num.toString()" [attr.data-rating]="num" (click)="setRatingScaleAnswer(num)">
104+
{{num}}
105+
</button>
106+
</div>
107+
<ng-template #ratingScaleRow>
108+
<div class="rating-scale-keypad rating-scale-keypad--row">
109+
<button type="button" class="rating-scale-button rating-scale-button--line" *ngFor="let num of ratingScaleNumbers" [class.selected]="answer.value === num.toString()" (click)="setRatingScaleAnswer(num)">
110+
{{num}}
111+
</button>
112+
</div>
113+
</ng-template>
114+
</ng-container>
115+
</ng-container>
88116
</ng-container>
89-
</ng-container>
90-
<ng-container *ngSwitchCase="'grade'">
91-
<p><b i18n>Submitted answer:</b></p>
92-
<td-markdown [content]="answer?.value?.text || answer?.value"></td-markdown>
93-
<mat-radio-group [(ngModel)]="grade" [disabled]="question?.type === 'select' || question?.type === 'selectMultiple'">
94-
<mat-radio-button [value]="1" class="planet-radio-button" i18n>Correct</mat-radio-button>
95-
<mat-radio-button [value]="0" class="planet-radio-button" i18n>Incorrect</mat-radio-button>
96-
</mat-radio-group>
97-
<mat-form-field class="full-width mat-form-field-type-no-underline">
98-
<mat-label i18n>Comment</mat-label>
99-
<planet-markdown-textbox class="full-width" [(ngModel)]="comment"></planet-markdown-textbox>
100-
</mat-form-field>
101-
</ng-container>
102-
<ng-container *ngSwitchCase="'view'">
103-
<p><b i18n>Response:</b></p>
104-
<td-markdown [content]="answer?.value?.text || answer?.value"></td-markdown>
105-
<ng-container *ngIf="grade>=0">
106-
<p><b i18n>Grade:</b></p>
107-
<p *ngIf="grade===1" i18n>Correct</p>
108-
<p *ngIf="grade===0" i18n>Incorrect</p>
117+
<ng-container *ngSwitchCase="'grade'">
118+
<p><b i18n>Submitted answer:</b></p>
119+
<td-markdown [content]="answer?.value?.text || answer?.value"></td-markdown>
120+
<mat-radio-group [(ngModel)]="grade" [disabled]="question?.type === 'select' || question?.type === 'selectMultiple'">
121+
<mat-radio-button [value]="1" class="planet-radio-button" i18n>Correct</mat-radio-button>
122+
<mat-radio-button [value]="0" class="planet-radio-button" i18n>Incorrect</mat-radio-button>
123+
</mat-radio-group>
124+
<mat-form-field class="full-width mat-form-field-type-no-underline">
125+
<mat-label i18n>Comment</mat-label>
126+
<planet-markdown-textbox class="full-width" [(ngModel)]="comment"></planet-markdown-textbox>
127+
</mat-form-field>
109128
</ng-container>
110-
<ng-container *ngIf="comment">
111-
<p><b i18n>Feedback:</b></p>
112-
<td-markdown [content]="comment"></td-markdown>
129+
<ng-container *ngSwitchCase="'view'">
130+
<p><b i18n>Response:</b></p>
131+
<td-markdown [content]="answer?.value?.text || answer?.value"></td-markdown>
132+
<ng-container *ngIf="grade>=0">
133+
<p><b i18n>Grade:</b></p>
134+
<p *ngIf="grade===1" i18n>Correct</p>
135+
<p *ngIf="grade===0" i18n>Incorrect</p>
136+
</ng-container>
137+
<ng-container *ngIf="comment">
138+
<p><b i18n>Feedback:</b></p>
139+
<td-markdown [content]="comment"></td-markdown>
140+
</ng-container>
113141
</ng-container>
114-
</ng-container>
142+
</div>
115143
</div>
116144
<div class="v-align-center action-buttons">
117145
<button
@@ -124,8 +152,8 @@
124152
</button>
125153
<button
126154
*ngIf="mode === 'take'"
127-
mat-raised-button
128-
color="accent"
155+
mat-stroked-button
156+
color="primary"
129157
(click)="nextQuestion({ isFinish: true })"
130158
[disabled]="!isComplete || !answer.valid || grade === undefined || grade === null"
131159
i18n>

0 commit comments

Comments
 (0)