diff --git a/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html b/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html index 7a4eaa4bf04..11a2b8a133f 100644 --- a/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html +++ b/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html @@ -1,5 +1,34 @@ + +
+

+ inventory_2  +

+ +
+
+ -
+

crop_16_9  @@ -8,50 +37,82 @@

    @for (bucketDataPoint of choice.choiceDataPoints; track $index) {
  • -
    +
    inventory_2 -
    -
    + + person{{ bucketDataPoint.getCount() }} -
    +
  • }
} @else { -
- do_not_disturb -
Not moved by any students
+
+ do_not_disturb + Not moved by any students
}
-

Bucket Frequency

-
- @if (choiceData.length > 0) { +

Choice Frequency

+ @if (choiceData.length > 0) { +

Number of times each item crop_16_9 was moved into the different buckets inventory_2.

+
+ Organize by: + + + crop_16_9  + Item + + + inventory_2  + Bucket + + +
+
+
- @for (choice of choiceData; track $index) { -
- -
+ @if (viewMode === 'choice') { + @for (choice of choiceData; track $index) { +
+ +
+ } + } @else { + @for (bucket of bucketData; track $index) { +
+ +
+ } }
- } @else { -
- Your students' choices will show up here when they complete the activity. -
- } -
+
+ } @else { +
+ Your students' choices will show up here when they complete the activity. +
+ }
diff --git a/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.scss b/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.scss index 9fd95b7f60d..63daaa2cf85 100644 --- a/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.scss +++ b/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.scss @@ -1,16 +1,24 @@ @reference "tailwindcss"; +:host { + --mat-button-toggle-divider-color: transparent; + --mat-button-toggle-height: 32px; + .mat-button-toggle-group-appearance-standard, .mat-button-toggle-appearance-standard { + @apply rounded-md; + } +} + h3, .mat-subtitle-1 { margin-bottom: 8px; margin-top: 0; } -.choice { +.choice-card, .bucket-card { @apply p-2 mb-2 rounded-md; } -.bucket { +.choice-row, .bucket-row { @apply flex gap-1 px-2 py-1 mt-1 rounded-md bg-white border border-neutral-200 text-sm items-start justify-between; } diff --git a/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.spec.ts b/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.spec.ts index 69a031f0963..a14b1085961 100644 --- a/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.spec.ts +++ b/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.spec.ts @@ -51,40 +51,66 @@ describe('MatchSummaryDisplayComponent', () => { expect(component).toBeTruthy(); }); - it('should display one card per unique choice', () => { - expect(fixtureQueryAll(fixture, '.choice').length).toEqual(5); - }); + describe('Choice view', () => { + it('should display one card per unique choice', () => { + expect(fixtureQueryAll(fixture, '.choice-card').length).toEqual(5); + }); - it('should order choices by total count descending then alphabetically', () => { - const cards = fixtureQueryAll(fixture, '.choice'); - const labels = Array.from(cards).map((el) => el.querySelector('h3')?.textContent?.trim()); - expect(labels[0]).toContain('Choice B'); - expect(labels[1]).toContain('Choice D'); - expect(labels[2]).toContain('Choice C'); - expect(labels[3]).toContain('Choice E'); - expect(labels[4]).toContain('Choice A'); - }); + it('should order choices by total count descending then alphabetically', () => { + const cards = fixtureQueryAll(fixture, '.choice-card'); + const labels = Array.from(cards).map((el) => el.querySelector('h3')?.textContent?.trim()); + expect(labels[0]).toContain('Choice B'); + expect(labels[1]).toContain('Choice D'); + expect(labels[2]).toContain('Choice C'); + expect(labels[3]).toContain('Choice E'); + expect(labels[4]).toContain('Choice A'); + }); - it('should show bucket rows sorted by count within each choice', () => { - const cards = fixtureQueryAll(fixture, '.choice'); - const choiceDCard = cards[1]; - const bucketRows = choiceDCard.querySelectorAll('.bucket'); - expect(bucketRows.length).toEqual(2); - expect(bucketRows[0].textContent).toContain('Bucket 2'); - expect(bucketRows[0].textContent).toContain('2'); - }); + it('should show bucket rows sorted by count within each choice', () => { + const cards = fixtureQueryAll(fixture, '.choice-card'); + const choiceDCard = cards[1]; + const bucketRows = choiceDCard.querySelectorAll('.bucket-row'); + expect(bucketRows.length).toEqual(2); + expect(bucketRows[0].textContent).toContain('Bucket 2'); + expect(bucketRows[0].textContent).toContain('2'); + }); + + it('should show the correct count for Choice B in Bucket 1', () => { + const cards = fixtureQueryAll(fixture, '.choice-card'); + const choiceBCard = cards[0]; + expect(choiceBCard.textContent).toContain('3'); + }); - it('should show the correct count for Choice B in Bucket 1', () => { - const cards = fixtureQueryAll(fixture, '.choice'); - const choiceBCard = cards[0]; - expect(choiceBCard.textContent).toContain('3'); + it('should show "Not moved by any students" for choices left in the source bucket', () => { + const cards = fixtureQueryAll(fixture, '.choice-card'); + const choiceACard = cards[4]; + expect(choiceACard.textContent).toContain('Not moved by any students'); + expect(choiceACard.querySelectorAll('.bucket-row').length).toEqual(1); + }); }); - it('should show "Not moved by any students" for choices left in the source bucket', () => { - const cards = fixtureQueryAll(fixture, '.choice'); - const choiceACard = cards[4]; - expect(choiceACard.textContent).toContain('Not moved by any students'); - expect(choiceACard.querySelectorAll('.bucket').length).toEqual(1); + describe('Bucket view', () => { + beforeEach(() => { + component.viewMode = 'bucket'; + fixture.detectChanges(); + }); + + it('should display one card per unique non-source bucket', () => { + expect(fixtureQueryAll(fixture, '.bucket-card').length).toBe(2); + }); + + it('should show choices sorted by count within each bucket', () => { + const cards = fixtureQueryAll(fixture, '.bucket-card'); + const bucket1Card = cards[0]; + const choiceRows = bucket1Card.querySelectorAll('.choice-row'); + expect(choiceRows.length).toBe(3); + expect(choiceRows[0].textContent).toContain('Choice B'); + expect(choiceRows[0].textContent).toContain('3'); + expect(choiceRows[1].textContent).toContain('Choice C'); + expect(choiceRows[1].textContent).toContain('2'); + expect(choiceRows[2].textContent).toContain('Choice D'); + expect(choiceRows[2].textContent).toContain('1'); + }); }); }); diff --git a/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.ts b/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.ts index ddb2039f1de..0d36012a0df 100644 --- a/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.ts +++ b/src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.ts @@ -1,12 +1,16 @@ import { CommonModule } from '@angular/common'; import { Component, Input, OnInit } from '@angular/core'; -import { ChoiceData, MatchSummaryData } from '../summary-data/MatchSummaryData'; +import { MatchContent } from '../../../components/match/MatchContent'; +import { BucketData, ChoiceData, MatchSummaryData } from '../summary-data/MatchSummaryData'; import { MatchSummaryDataPoint } from '../summary-data/MatchSummaryDataPoint'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatIconModule } from '@angular/material/icon'; import { TeacherSummaryDisplayComponent } from '../teacher-summary-display.component'; +export type SummaryViewMode = 'choice' | 'bucket'; + @Component({ - imports: [CommonModule, MatIconModule], + imports: [CommonModule, MatButtonToggleModule, MatIconModule], selector: 'match-summary-display', styleUrls: [ './match-summary-display.component.scss', @@ -15,21 +19,33 @@ import { TeacherSummaryDisplayComponent } from '../teacher-summary-display.compo templateUrl: './match-summary-display.component.html' }) export class MatchSummaryDisplayComponent extends TeacherSummaryDisplayComponent implements OnInit { + protected bucketData: { value: string; choices: MatchSummaryDataPoint[] }[] = []; protected choiceData: ChoiceData[] = []; @Input() expanded: boolean; + protected isChoiceReuseMatch: boolean; private matchSummaryData: MatchSummaryData; + viewMode: SummaryViewMode = 'choice'; ngOnInit(): void { + this.setIsChoiceReuseMatch(); this.generateSummary(); } + private setIsChoiceReuseMatch(): void { + this.isChoiceReuseMatch = ( + this.projectService.getComponent(this.nodeId, this.componentId) as MatchContent + ).choiceReuseEnabled; + } + private generateSummary(): void { this.getLatestWork().subscribe((componentStates) => { + this.bucketData = []; this.choiceData = []; this.matchSummaryData = new MatchSummaryData( this.projectService.injectAssetPaths(componentStates) ); this.setChoiceData(); + this.setBucketData(); }); } @@ -37,7 +53,7 @@ export class MatchSummaryDisplayComponent extends TeacherSummaryDisplayComponent this.matchSummaryData.getChoicesData().forEach((choice) => { this.choiceData.push({ choiceValue: choice.choiceValue, - choiceDataPoints: choice.choiceDataPoints.sort(this.sortBuckets) + choiceDataPoints: choice.choiceDataPoints.sort(this.sortByCount) }); }); this.choiceData.sort(this.sortChoices); @@ -52,7 +68,16 @@ export class MatchSummaryDisplayComponent extends TeacherSummaryDisplayComponent return countDiff !== 0 ? countDiff : a.choiceValue.localeCompare(b.choiceValue); }; - private sortBuckets(a: MatchSummaryDataPoint, b: MatchSummaryDataPoint): number { + protected setBucketData(): void { + this.matchSummaryData.getBucketsData().forEach((bucket: BucketData) => { + this.bucketData.push({ + value: bucket.bucketValue, + choices: bucket.bucketDataPoints.sort(this.sortByCount) + }); + }); + } + + private sortByCount(a: MatchSummaryDataPoint, b: MatchSummaryDataPoint): number { return b.getCount() - a.getCount(); } diff --git a/src/assets/wise5/directives/teacher-summary-display/summary-data/MatchSummaryData.ts b/src/assets/wise5/directives/teacher-summary-display/summary-data/MatchSummaryData.ts index 46c6c122c79..be1050073cc 100644 --- a/src/assets/wise5/directives/teacher-summary-display/summary-data/MatchSummaryData.ts +++ b/src/assets/wise5/directives/teacher-summary-display/summary-data/MatchSummaryData.ts @@ -3,6 +3,7 @@ import { MatchSummaryDataPoint } from './MatchSummaryDataPoint'; import { SummaryData } from '../../summary-display/summary-data/SummaryData'; export type ChoiceData = { choiceValue: string; choiceDataPoints: MatchSummaryDataPoint[] }; +export type BucketData = { bucketValue: string; bucketDataPoints: MatchSummaryDataPoint[] }; /** * Summary data for all choices, each with a breakdown per bucket @@ -19,14 +20,34 @@ export class MatchSummaryData extends SummaryData { return this.choicesData; } + getBucketsData(): BucketData[] { + const bucketsMap = new Map(); + this.choicesData.forEach((choice) => { + choice.choiceDataPoints.forEach((point) => { + const bucketValue = point.getBucketValue(); + if (!bucketsMap.has(bucketValue)) { + bucketsMap.set(bucketValue, { bucketValue, bucketDataPoints: [] }); + } + bucketsMap.get(bucketValue).bucketDataPoints.push(point); + }); + }); + return Array.from(bucketsMap.values()).sort( + (a, b) => this.getTotalCount(b) - this.getTotalCount(a) + ); + } + + private getTotalCount(bucket: BucketData): number { + return bucket.bucketDataPoints.reduce((sum, point) => sum + point.getCount(), 0); + } + private extractChoiceData(componentStates: ComponentState[]): void { componentStates.forEach((componentState) => { componentState.studentData.buckets.forEach((bucketStudentData, index) => { if (index === 0) { - bucketStudentData.items.forEach((item) => this.registerChoice(item.value)); + bucketStudentData.items.forEach((choice) => this.registerChoice(choice.value)); } else { - bucketStudentData.items.forEach((item) => { - this.extractBucketDataPerChoice(item.value, bucketStudentData.value); + bucketStudentData.items.forEach((choice) => { + this.extractBucketDataPerChoice(choice.value, bucketStudentData.value); }); } }); diff --git a/src/messages.xlf b/src/messages.xlf index 33e39bf173f..7a72054bf24 100644 --- a/src/messages.xlf +++ b/src/messages.xlf @@ -16263,7 +16263,15 @@ Are you sure you want to proceed? src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html - 4,5 + 11,14 + + + src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html + 33,34 + + + src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html + 81,83 @@ -23256,42 +23264,71 @@ If this problem continues, let your teacher know and move on to the next activit Bucket src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html - 12,15 + 4,5 - - - Not moved src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html - 26,27 + 41,44 + + + src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html + 85,88 Not moved by any students src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html - 27,33 + 56,62 - - Bucket Frequency + + Choice Frequency src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html - 34,35 + 63,65 Number of times each item crop_16_9 was moved into the different buckets inventory_2. src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html - 38,41 + 67,70 + + + + Organize by: + + src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html + 71,73 + + + + Organize by + + src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html + 76,79 + + + + Organize by choice + + src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html + 79,80 + + + + Organize by bucket + + src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html + 83,84 Your students' choices will show up here when they complete the activity. src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html - 53,58 + 115,119