From 3479f137d6c49670702b8bbe01e1caa3bf877a47 Mon Sep 17 00:00:00 2001 From: Hiroki Terashima Date: Fri, 6 Feb 2026 16:03:43 -0800 Subject: [PATCH 1/3] feat(IdeasSummary): Show student responses --- .../idea-summary/idea-summary.component.html | 13 + .../idea-summary.component.spec.ts | 267 ++++++++++++++++++ .../idea-summary/idea-summary.component.ts | 82 ++++++ .../ideas-summary.component.html | 34 +-- .../ideas-summary.component.spec.ts | 6 +- .../ideas-summary.component.ts | 7 +- src/assets/wise5/services/cRaterService.ts | 1 + src/messages.xlf | 14 +- 8 files changed, 394 insertions(+), 30 deletions(-) create mode 100644 src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.html create mode 100644 src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.spec.ts create mode 100644 src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.ts diff --git a/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.html b/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.html new file mode 100644 index 00000000000..d0b4ae68bd2 --- /dev/null +++ b/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.html @@ -0,0 +1,13 @@ +
+ {{ idea.id }}. {{ idea.text }} (person{{ idea.count }}) + @if (expanded) { + expand_less +
+ @for (response of responses; track response.timestamp) { + person{{ response.text }}
+ } +
+ } @else { + expand_more + } +
diff --git a/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.spec.ts b/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.spec.ts new file mode 100644 index 00000000000..daafdcc650c --- /dev/null +++ b/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.spec.ts @@ -0,0 +1,267 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockProviders } from 'ng-mocks'; +import { Observable, Subject } from 'rxjs'; +import { AnnotationService } from '../../../services/annotationService'; +import { ConfigService } from '../../../services/configService'; +import { CRaterService } from '../../../services/cRaterService'; +import { SummaryService } from '../../../components/summary/summaryService'; +import { TeacherDataService } from '../../../services/teacherDataService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; +import { IdeaSummaryComponent } from './idea-summary.component'; +import { ComponentState } from '../../../../../app/domain/componentState'; +import { Annotation } from '../../../common/Annotation'; +import { DataService } from '../../../../../app/services/data.service'; +import { ProjectService } from '../../../services/projectService'; + +let component: IdeaSummaryComponent; +let fixture: ComponentFixture; +let annotationService: AnnotationService; +let projectService: TeacherProjectService; + +class MockProjectService { + private projectSavedSource: Subject = new Subject(); + public readonly projectSaved$: Observable = this.projectSavedSource.asObservable(); + getComponent(): any { + return null; + } +} + +describe('IdeaSummaryComponent', () => { + beforeEach(async () => { + const dataServiceSpy = jasmine.createSpyObj('DataService', ['getCurrentNode']); + await TestBed.configureTestingModule({ + imports: [IdeaSummaryComponent], + providers: [ + { provide: DataService, useValue: dataServiceSpy }, + { provide: ProjectService, useClass: MockProjectService }, + { provide: TeacherProjectService, useClass: MockProjectService }, + MockProviders( + AnnotationService, + ConfigService, + CRaterService, + TeacherDataService, + SummaryService + ) + ] + }).compileComponents(); + + projectService = TestBed.inject(TeacherProjectService); + annotationService = TestBed.inject(AnnotationService); + fixture = TestBed.createComponent(IdeaSummaryComponent); + component = fixture.componentInstance; + + // Set up default inputs + component.componentId = 'component1'; + component.nodeId = 'node1'; + component.idea = { + id: 'idea1', + text: 'Test Idea', + count: 5 + }; + }); + + describe('initial state', () => { + it('should initialize with expanded as false', () => { + expect(component['expanded']).toBe(false); + }); + + it('should initialize with empty responses array', () => { + expect(component['responses']).toEqual([]); + }); + }); + + describe('when expanding for the first time', () => { + beforeEach(() => { + component['expanded'] = false; + component['responses'] = []; + }); + + it('should not fetch responses again when already loaded', async () => { + component['responses'] = [{ text: 'Existing response', timestamp: 123456 }]; + + const getComponentSpy = spyOn(projectService, 'getComponent'); + const getLatestWorkSpy = spyOn(component, 'getLatestWork'); + + await component['toggleDetails'](); + + expect(getComponentSpy).not.toHaveBeenCalled(); + expect(getLatestWorkSpy).not.toHaveBeenCalled(); + }); + }); + + describe('getDGResponsesWithIdea()', () => { + it('should return responses with the specified idea', () => { + const states = [ + new ComponentState({ + workgroupId: 1, + studentData: { + responses: [ + { text: 'Student response 1', timestamp: 111 }, + { text: 'Computer response 1', ideas: [{ detected: true, name: 'idea1' }] } + ] + } + }), + new ComponentState({ + workgroupId: 2, + studentData: { + responses: [ + { text: 'Student response 2', timestamp: 222 }, + { text: 'Computer response 2', ideas: [{ detected: true, name: 'idea2' }] } + ] + } + }) + ]; + + const responses = component['getDGResponsesWithIdea'](states, 'idea1'); + expect(responses.length).toBe(1); + expect(responses[0].text).toBe('Student response 1'); + }); + + it('should return only one response per workgroup', () => { + const states = [ + new ComponentState({ + workgroupId: 1, + studentData: { + responses: [ + { text: 'Student response 1a', timestamp: 111 }, + { text: 'Computer response 1a', ideas: [{ detected: true, name: 'idea1' }] } + ] + } + }), + new ComponentState({ + workgroupId: 1, + studentData: { + responses: [ + { text: 'Student response 1b', timestamp: 222 }, + { text: 'Computer response 1b', ideas: [{ detected: true, name: 'idea1' }] } + ] + } + }) + ]; + + const responses = component['getDGResponsesWithIdea'](states, 'idea1'); + expect(responses.length).toBe(1); + }); + + it('should return empty array when no ideas match', () => { + const states = [ + new ComponentState({ + workgroupId: 1, + studentData: { + responses: [ + { text: 'Student response', timestamp: 111 }, + { text: 'Computer response', ideas: [{ detected: true, name: 'idea2' }] } + ] + } + }) + ]; + + const responses = component['getDGResponsesWithIdea'](states, 'idea1'); + expect(responses.length).toBe(0); + }); + + it('should skip responses where idea is not detected', () => { + const states = [ + new ComponentState({ + workgroupId: 1, + studentData: { + responses: [ + { text: 'Student response', timestamp: 111 }, + { text: 'Computer response', ideas: [{ detected: false, name: 'idea1' }] } + ] + } + }) + ]; + + const responses = component['getDGResponsesWithIdea'](states, 'idea1'); + expect(responses.length).toBe(0); + }); + }); + + describe('getORResponsesWithIdea()', () => { + it('should return responses with matching annotations', () => { + const states = [ + new ComponentState({ + id: 1, + workgroupId: 1, + clientSaveTime: 123456, + studentData: { response: 'Student answer 1' } + }), + new ComponentState({ + id: 2, + workgroupId: 2, + clientSaveTime: 234567, + studentData: { response: 'Student answer 2' } + }) + ]; + + const annotations = [ + new Annotation({ + studentWorkId: 1, + data: { ideas: [{ detected: true, name: 'idea1' }] } + }) + ]; + + spyOn(annotationService, 'getAnnotationsByNodeIdComponentId').and.returnValue(annotations); + const responses = component['getORResponsesWithIdea'](states, 'idea1'); + expect(responses.length).toBe(1); + expect(responses[0].text).toBe('Student answer 1'); + expect(responses[0].timestamp).toBe(123456); + }); + + it('should return empty array when no annotations match', () => { + const states = [ + new ComponentState({ + id: 1, + workgroupId: 1, + clientSaveTime: 123456, + studentData: { response: 'Student answer' } + }) + ]; + + const annotations = [ + new Annotation({ + studentWorkId: 2, + data: { ideas: [{ detected: true, name: 'idea1' }] } + }) + ]; + + spyOn(annotationService, 'getAnnotationsByNodeIdComponentId').and.returnValue(annotations); + const responses = component['getORResponsesWithIdea'](states, 'idea1'); + expect(responses.length).toBe(0); + }); + + it('should filter annotations by idea name and detected status', () => { + const states = [ + new ComponentState({ + id: 1, + workgroupId: 1, + clientSaveTime: 123456, + studentData: { response: 'Student answer 1' } + }), + new ComponentState({ + id: 2, + workgroupId: 2, + clientSaveTime: 234567, + studentData: { response: 'Student answer 2' } + }) + ]; + + const annotations = [ + new Annotation({ + studentWorkId: 1, + data: { ideas: [{ detected: true, name: 'idea1' }] } + }), + new Annotation({ + studentWorkId: 2, + data: { ideas: [{ detected: false, name: 'idea1' }] } + }) + ]; + + spyOn(annotationService, 'getAnnotationsByNodeIdComponentId').and.returnValue(annotations); + const responses = component['getORResponsesWithIdea'](states, 'idea1'); + expect(responses.length).toBe(1); + expect(responses[0].text).toBe('Student answer 1'); + }); + }); +}); diff --git a/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.ts b/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.ts new file mode 100644 index 00000000000..3f474c4bcb5 --- /dev/null +++ b/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.ts @@ -0,0 +1,82 @@ +import { Component, Input } from '@angular/core'; +import { MatIcon } from '@angular/material/icon'; +import { TeacherSummaryDisplayComponent } from '../teacher-summary-display.component'; +import { firstValueFrom } from 'rxjs'; +import { ComponentState } from '../../../../../app/domain/componentState'; + +interface IdeaCount { + id: string; + text: string; + count: number; +} + +interface Response { + text: string; + timestamp: number; +} + +@Component({ + imports: [MatIcon], + selector: 'idea-summary', + styles: ` + .mat-icon { + vertical-align: middle; + } + `, + templateUrl: './idea-summary.component.html' +}) +export class IdeaSummaryComponent extends TeacherSummaryDisplayComponent { + @Input() componentId: string; + @Input() idea: IdeaCount; + @Input() nodeId: string; + + protected expanded: boolean = false; + protected responses: Response[] = []; + + protected async toggleDetails(): Promise { + this.expanded = !this.expanded; + if (this.responses.length === 0) { + const component = this.projectService.getComponent(this.nodeId, this.componentId); + const states = await firstValueFrom(this.getLatestWork()); + if (component.type === 'DialogGuidance') { + this.responses = this.getDGResponsesWithIdea(states, this.idea.id); + } else if (component.type === 'OpenResponse') { + this.responses = this.getORResponsesWithIdea(states, this.idea.id); + } + if (this.responses.length > 2) { + this.responses = this.responses.slice(0, 2); // only show 2 responses max + } + } + } + + private getDGResponsesWithIdea(states: ComponentState[], ideaId: string): Response[] { + const responsesWithIdea: Response[] = []; + const workgroupsProcessed = []; // ensure we only add one response per workgroup + states.forEach((state) => { + state.studentData.responses.forEach((response, index, responses) => { + if (workgroupsProcessed.includes(state.workgroupId)) return; + if (response?.ideas?.some((idea) => idea.detected && idea.name === ideaId)) { + // computer responses contain ideas detected, but we want the actual student response + // which is before the computer response + responsesWithIdea.push(responses[index - 1]); + workgroupsProcessed.push(state.workgroupId); + } + }); + }); + return responsesWithIdea; + } + + private getORResponsesWithIdea(states: ComponentState[], ideaId: string): Response[] { + const annotations = this.annotationService + .getAnnotationsByNodeIdComponentId(this.nodeId, this.componentId) + .filter((annotation) => + annotation.data.ideas?.some((idea) => idea.detected && idea.name === ideaId) + ); + return states + .filter((state) => annotations.some((annotation) => annotation.studentWorkId === state.id)) + .map((state) => ({ + text: state.studentData.response, + timestamp: state.clientSaveTime + })); + } +} diff --git a/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.html b/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.html index 80b85af160d..54ce3b80fed 100644 --- a/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.html +++ b/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.html @@ -1,10 +1,3 @@ - -
- {{ idea.id }}. {{ idea.text }} (person{{ idea.count }}) -
-
-

Student Ideas Detected

@if (hasWarning) {

{{ warningMessage }}

@@ -16,9 +9,12 @@

Most Common:

    @for (idea of mostCommonIdeas; track idea.id) {
  • -
  • } @@ -29,9 +25,12 @@

    Least Common:

      @for (idea of leastCommonIdeas; track idea.id) {
    • -
    • } @@ -43,9 +42,12 @@

      All Ideas:

        @for (idea of allIdeas; track idea.id) {
      • -
      • } diff --git a/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.spec.ts b/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.spec.ts index a48e4e8f9b3..71792634748 100644 --- a/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.spec.ts +++ b/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.spec.ts @@ -13,13 +13,15 @@ import { TeacherDataService } from '../../../services/teacherDataService'; import { TeacherProjectService } from '../../../services/teacherProjectService'; import { TestBed } from '@angular/core/testing'; import { Annotation } from '../../../common/Annotation'; +import { IdeaSummaryComponent } from '../idea-summary/idea-summary.component'; +import { MockComponent } from 'ng-mocks'; let component: IdeasSummaryComponent; let fixture: ComponentFixture; describe('IdeasSummaryDisplayComponent for Dialog Guidance component', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [IdeasSummaryComponent], + imports: [IdeasSummaryComponent, MockComponent(IdeaSummaryComponent)], providers: [ MockProviders( AnnotationService, @@ -55,7 +57,7 @@ describe('IdeasSummaryDisplayComponent for Dialog Guidance component', () => { describe('IdeasSummaryDisplayComponent for Open Response component', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [IdeasSummaryComponent], + imports: [IdeasSummaryComponent, MockComponent(IdeaSummaryComponent)], providers: [ MockProviders( AnnotationService, diff --git a/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.ts b/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.ts index ce2f993d5f0..0862bb59b77 100644 --- a/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.ts +++ b/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.ts @@ -9,15 +9,15 @@ import { DialogGuidanceSummaryData } from '../summary-data/DialogGuidanceSummary import { IdeaData } from '../../../components/common/cRater/IdeaData'; import { IdeasSortingService } from '../../../services/ideasSortingService'; import { IdeasSummaryData } from '../summary-data/IdeasSummaryData'; -import { MatIconModule } from '@angular/material/icon'; import { OpenResponseSummaryData } from '../summary-data/OpenResponseSummaryData'; import { SummaryService } from '../../../components/summary/summaryService'; import { TeacherDataService } from '../../../services/teacherDataService'; import { TeacherProjectService } from '../../../services/teacherProjectService'; import { TeacherSummaryDisplayComponent } from '../teacher-summary-display.component'; +import { IdeaSummaryComponent } from '../idea-summary/idea-summary.component'; @Component({ - imports: [CommonModule, MatIconModule], + imports: [CommonModule, IdeaSummaryComponent], providers: [IdeasSortingService], selector: 'ideas-summary', styles: ` @@ -26,9 +26,6 @@ import { TeacherSummaryDisplayComponent } from '../teacher-summary-display.compo margin-bottom: 8px; margin-top: 0; } - .mat-icon { - vertical-align: middle; - } ul { list-style-type: none; margin-block-start: 0; diff --git a/src/assets/wise5/services/cRaterService.ts b/src/assets/wise5/services/cRaterService.ts index cda0e800f89..dbbe3d6c781 100644 --- a/src/assets/wise5/services/cRaterService.ts +++ b/src/assets/wise5/services/cRaterService.ts @@ -262,6 +262,7 @@ export class CRaterService { getCRaterRubric(nodeId: string, componentId: string, componentType?: string): CRaterRubric { const componentContent = this.projectService.getComponent(nodeId, componentId); + componentType = componentType ?? componentContent.type; let rubricContent; if (componentType === 'OpenResponse') { rubricContent = (componentContent as OpenResponseContent).cRater?.rubric; diff --git a/src/messages.xlf b/src/messages.xlf index 57fb072b317..9bf00ebba98 100644 --- a/src/messages.xlf +++ b/src/messages.xlf @@ -21875,49 +21875,49 @@ If this problem continues, let your teacher know and move on to the next activit Student Ideas Detected src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.html - 8,10 + 1,3 Most Common: src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.html - 15,17 + 8,10 Least Common: src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.html - 28,30 + 24,26 All Ideas: src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.html - 42,44 + 41,43 Hide all ideas src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.html - 53,55 + 55,57 Show all ideas src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.html - 55,58 + 57,60 Your students' ideas will show up here as they are detected in the activity. src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.html - 58,60 + 60,62 From 22e237acd5c8c3234ad5c85aa4efd57208a8d555 Mon Sep 17 00:00:00 2001 From: Jonathan Lim-Breitbart Date: Sun, 15 Feb 2026 21:55:36 -0800 Subject: [PATCH 2/3] Updated styles and utilize MatExpansionPanel --- .../library-project-details.component.scss | 9 ++-- .../idea-summary/idea-summary.component.html | 33 ++++++++---- .../idea-summary/idea-summary.component.scss | 53 +++++++++++++++++++ .../idea-summary.component.spec.ts | 5 -- .../idea-summary/idea-summary.component.ts | 14 ++--- .../ideas-summary.component.ts | 3 ++ 6 files changed, 88 insertions(+), 29 deletions(-) create mode 100644 src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.scss diff --git a/src/app/modules/library/library-project-details/library-project-details.component.scss b/src/app/modules/library/library-project-details/library-project-details.component.scss index 45e801753d7..cd09ede56f4 100644 --- a/src/app/modules/library/library-project-details/library-project-details.component.scss +++ b/src/app/modules/library/library-project-details/library-project-details.component.scss @@ -1,9 +1,6 @@ @use '@angular/material' as mat; - @use 'style/abstracts/functions'; - -.library-project-details { -} +@reference "tailwindcss"; .info-block { padding: 12px; @@ -58,6 +55,10 @@ } } +.notice { + @apply max-w-none; +} + discourse-category-activity, unit-tags { margin-bottom: 12px; display: block; diff --git a/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.html b/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.html index d0b4ae68bd2..e0267030453 100644 --- a/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.html +++ b/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.html @@ -1,13 +1,24 @@ -
        - {{ idea.id }}. {{ idea.text }} (person{{ idea.count }}) - @if (expanded) { - expand_less -
        + + + + {{ idea.id }}. {{ idea.text }} + + + + person{{ idea.count }} + + + +
        + Sample responses: +
          @for (response of responses; track response.timestamp) { - person{{ response.text }}
          +
        • "{{ response.text }}"
        • } -
        - } @else { - expand_more - } -
        +
      + + diff --git a/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.scss b/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.scss new file mode 100644 index 00000000000..5c93dd0c34f --- /dev/null +++ b/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.scss @@ -0,0 +1,53 @@ +@import "tailwindcss"; + +idea-summary { + --mat-expansion-legacy-header-indicator-display: none; + --mat-expansion-header-indicator-display: inline-block; + // TODO: convert to expansion-overrides mixin when supported + --mat-expansion-header-collapsed-state-height: auto; + --mat-expansion-header-expanded-state-height: auto; + --mat-expansion-container-elevation-shadow: none; + + ul { + margin: 0; + padding: 0; + list-style: none; + } + + li { + @apply mt-1; + } + + .mat-expansion-panel { + @apply bg-gray-100; + } + + .mat-expansion-panel-header { + @apply py-1 px-2; + } + + .mat-expansion-panel-header-title { + flex-grow: 15; + } + + .mat-expansion-panel-header-description { + align-items: start; + flex-grow: 1; + } + + .mat-content.mat-content-hide-toggle { + margin-inline-end: 0; + + .mat-expansion-panel-header-description { + margin-inline-end: 0; + } + } + + .mat-expansion-indicator { + align-self: start; + } + + .mat-expansion-panel-body { + @apply pb-2 px-2; + } +} diff --git a/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.spec.ts b/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.spec.ts index daafdcc650c..b4801ba6c2d 100644 --- a/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.spec.ts +++ b/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.spec.ts @@ -61,10 +61,6 @@ describe('IdeaSummaryComponent', () => { }); describe('initial state', () => { - it('should initialize with expanded as false', () => { - expect(component['expanded']).toBe(false); - }); - it('should initialize with empty responses array', () => { expect(component['responses']).toEqual([]); }); @@ -72,7 +68,6 @@ describe('IdeaSummaryComponent', () => { describe('when expanding for the first time', () => { beforeEach(() => { - component['expanded'] = false; component['responses'] = []; }); diff --git a/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.ts b/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.ts index 3f474c4bcb5..98a25cd3691 100644 --- a/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.ts +++ b/src/assets/wise5/directives/teacher-summary-display/idea-summary/idea-summary.component.ts @@ -1,8 +1,9 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, ViewEncapsulation } from '@angular/core'; import { MatIcon } from '@angular/material/icon'; import { TeacherSummaryDisplayComponent } from '../teacher-summary-display.component'; import { firstValueFrom } from 'rxjs'; import { ComponentState } from '../../../../../app/domain/componentState'; +import { MatExpansionModule } from '@angular/material/expansion'; interface IdeaCount { id: string; @@ -16,13 +17,10 @@ interface Response { } @Component({ - imports: [MatIcon], + encapsulation: ViewEncapsulation.None, + imports: [MatExpansionModule, MatIcon], selector: 'idea-summary', - styles: ` - .mat-icon { - vertical-align: middle; - } - `, + styleUrl: './idea-summary.component.scss', templateUrl: './idea-summary.component.html' }) export class IdeaSummaryComponent extends TeacherSummaryDisplayComponent { @@ -30,11 +28,9 @@ export class IdeaSummaryComponent extends TeacherSummaryDisplayComponent { @Input() idea: IdeaCount; @Input() nodeId: string; - protected expanded: boolean = false; protected responses: Response[] = []; protected async toggleDetails(): Promise { - this.expanded = !this.expanded; if (this.responses.length === 0) { const component = this.projectService.getComponent(this.nodeId, this.componentId); const states = await firstValueFrom(this.getLatestWork()); diff --git a/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.ts b/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.ts index 0862bb59b77..d5a2d191eaa 100644 --- a/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.ts +++ b/src/assets/wise5/directives/teacher-summary-display/ideas-summary-display/ideas-summary.component.ts @@ -31,6 +31,9 @@ import { IdeaSummaryComponent } from '../idea-summary/idea-summary.component'; margin-block-start: 0; padding-inline-start: 0; } + li:not(:last-child) { + margin-bottom: 4px; + } `, templateUrl: 'ideas-summary.component.html' }) From 72b3098a7bc440395258c38561e1e6e7fb98201c Mon Sep 17 00:00:00 2001 From: Jonathan Lim-Breitbart Date: Tue, 17 Feb 2026 09:43:54 -0800 Subject: [PATCH 3/3] Fix Typescript compilation error --- src/assets/wise5/services/cRaterService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/assets/wise5/services/cRaterService.ts b/src/assets/wise5/services/cRaterService.ts index dbbe3d6c781..2b3cd3cccf4 100644 --- a/src/assets/wise5/services/cRaterService.ts +++ b/src/assets/wise5/services/cRaterService.ts @@ -262,9 +262,9 @@ export class CRaterService { getCRaterRubric(nodeId: string, componentId: string, componentType?: string): CRaterRubric { const componentContent = this.projectService.getComponent(nodeId, componentId); - componentType = componentType ?? componentContent.type; + const resolvedComponentType = componentType ?? componentContent.type; let rubricContent; - if (componentType === 'OpenResponse') { + if (resolvedComponentType === 'OpenResponse') { rubricContent = (componentContent as OpenResponseContent).cRater?.rubric; } else { rubricContent = componentContent.cRaterRubric;