From 1b9b54b2c4f307d5713f0d843c1e2c5b6bbdcf09 Mon Sep 17 00:00:00 2001 From: Geoffrey Kwan Date: Sun, 28 Apr 2024 13:36:42 -0400 Subject: [PATCH] refactor(Authoring): Create select component component --- .../select-component.component.html | 13 ++++ .../select-component.component.spec.ts | 29 ++++++++ .../select-component.component.ts | 66 +++++++++++++++++ .../select-component.harness.ts | 7 ++ .../select-step-and-component.component.html | 20 ++---- ...elect-step-and-component.component.spec.ts | 70 ++++++++++--------- .../select-step-and-component.component.ts | 61 +++------------- .../select-step-and-component.harness.ts | 2 + src/messages.xlf | 8 +-- 9 files changed, 173 insertions(+), 103 deletions(-) create mode 100644 src/app/authoring-tool/select-component/select-component.component.html create mode 100644 src/app/authoring-tool/select-component/select-component.component.spec.ts create mode 100644 src/app/authoring-tool/select-component/select-component.component.ts create mode 100644 src/app/authoring-tool/select-component/select-component.harness.ts diff --git a/src/app/authoring-tool/select-component/select-component.component.html b/src/app/authoring-tool/select-component/select-component.component.html new file mode 100644 index 00000000000..669893d39b8 --- /dev/null +++ b/src/app/authoring-tool/select-component/select-component.component.html @@ -0,0 +1,13 @@ + + Component + + + {{ componentIndex + 1 }}. {{ component.type }} + (This Component) + + + diff --git a/src/app/authoring-tool/select-component/select-component.component.spec.ts b/src/app/authoring-tool/select-component/select-component.component.spec.ts new file mode 100644 index 00000000000..e15d4b867cb --- /dev/null +++ b/src/app/authoring-tool/select-component/select-component.component.spec.ts @@ -0,0 +1,29 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { SelectComponentComponent } from './select-component.component'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StudentTeacherCommonServicesModule } from '../../student-teacher-common-services.module'; +import { SelectStepComponent } from '../select-step/select-step.component'; + +describe('SelectComponentComponent', () => { + let component: SelectComponentComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + BrowserAnimationsModule, + HttpClientTestingModule, + SelectStepComponent, + StudentTeacherCommonServicesModule + ] + }); + fixture = TestBed.createComponent(SelectComponentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/authoring-tool/select-component/select-component.component.ts b/src/app/authoring-tool/select-component/select-component.component.ts new file mode 100644 index 00000000000..04c632c8595 --- /dev/null +++ b/src/app/authoring-tool/select-component/select-component.component.ts @@ -0,0 +1,66 @@ +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Input, Output, SimpleChanges } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { ProjectService } from '../../../assets/wise5/services/projectService'; +import { ComponentContent } from '../../../assets/wise5/common/ComponentContent'; + +@Component({ + selector: 'select-component', + templateUrl: './select-component.component.html', + standalone: true, + imports: [CommonModule, FormsModule, MatFormFieldModule, MatSelectModule] +}) +export class SelectComponentComponent { + @Input() allowedComponentTypes: string[] = []; + @Output() componentChangedEvent: EventEmitter = new EventEmitter(); + @Input() componentId: string; + protected components: ComponentContent[] = []; + protected componentToIsAllowed: Map = new Map(); + @Input() nodeId: string; + @Input() thisComponentId: string; + + constructor(private projectService: ProjectService) {} + + ngOnChanges(changes: SimpleChanges): void { + if (changes.nodeId) { + this.nodeId = changes.nodeId.currentValue; + this.componentId = null; + this.calculateComponents(this.nodeId); + this.automaticallySetComponentIfPossible(this.nodeId); + } + } + + private calculateComponents(nodeId: string): void { + this.components = this.projectService.getComponents(nodeId); + for (const component of this.components) { + this.componentToIsAllowed.set( + component.id, + this.allowedComponentTypes.includes(component.type) + ); + } + } + + private automaticallySetComponentIfPossible(nodeId: string): void { + let numAllowedComponents = 0; + let allowedComponent = null; + for (const component of this.projectService.getComponents(nodeId)) { + if ( + this.allowedComponentTypes.includes(component.type) && + component.id !== this.thisComponentId + ) { + numAllowedComponents += 1; + allowedComponent = component; + } + } + if (numAllowedComponents === 1) { + this.componentId = allowedComponent.id; + this.componentChanged(); + } + } + + protected componentChanged(): void { + this.componentChangedEvent.emit(this.componentId); + } +} diff --git a/src/app/authoring-tool/select-component/select-component.harness.ts b/src/app/authoring-tool/select-component/select-component.harness.ts new file mode 100644 index 00000000000..2589c85632c --- /dev/null +++ b/src/app/authoring-tool/select-component/select-component.harness.ts @@ -0,0 +1,7 @@ +import { ComponentHarness } from '@angular/cdk/testing'; +import { MatSelectHarness } from '@angular/material/select/testing'; + +export class SelectComponentHarness extends ComponentHarness { + static hostSelector = 'select-component'; + getSelect = this.locatorFor(MatSelectHarness); +} diff --git a/src/app/authoring-tool/select-step-and-component/select-step-and-component.component.html b/src/app/authoring-tool/select-step-and-component/select-step-and-component.component.html index 57ad6c2a0dc..21749632598 100644 --- a/src/app/authoring-tool/select-step-and-component/select-step-and-component.component.html +++ b/src/app/authoring-tool/select-step-and-component/select-step-and-component.component.html @@ -3,16 +3,10 @@ [nodeId]="referenceComponent.nodeId" (stepChangedEvent)="stepChanged($event)" > - - Component - - - {{ componentIndex + 1 }}. {{ component.type }} - (This Component) - - - + diff --git a/src/app/authoring-tool/select-step-and-component/select-step-and-component.component.spec.ts b/src/app/authoring-tool/select-step-and-component/select-step-and-component.component.spec.ts index 30630d85a0f..9138a92bf0b 100644 --- a/src/app/authoring-tool/select-step-and-component/select-step-and-component.component.spec.ts +++ b/src/app/authoring-tool/select-step-and-component/select-step-and-component.component.spec.ts @@ -7,9 +7,7 @@ import { StudentTeacherCommonServicesModule } from '../../student-teacher-common import { SelectStepAndComponentComponent } from './select-step-and-component.component'; import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; -import { MatSelectHarness } from '@angular/material/select/testing'; import { SelectStepAndComponentHarness } from './select-step-and-component.harness'; -import { SelectStepComponent } from '../select-step/select-step.component'; let component: SelectStepAndComponentComponent; const componentId1 = 'component1'; @@ -33,14 +31,14 @@ describe('SelectStepAndComponentComponent', () => { HttpClientTestingModule, SelectStepAndComponentComponent, StudentTeacherCommonServicesModule - ], - providers: [ProjectService] + ] }).compileComponents(); fixture = TestBed.createComponent(SelectStepAndComponentComponent); loader = TestbedHarnessEnvironment.loader(fixture); component = fixture.componentInstance; component.referenceComponent = new ReferenceComponent(null, null); component.allowedComponentTypes = ['OpenResponse']; + component.thisComponentId = componentId1; projectService = TestBed.inject(ProjectService); spyOn(projectService, 'getStepNodeIds').and.returnValue(nodeIds); spyOn(projectService, 'getFlattenedProjectAsNodeIds').and.returnValue(nodeIds); @@ -58,41 +56,45 @@ describe('SelectStepAndComponentComponent', () => { SelectStepAndComponentHarness ); }); - selectComponent(); - stepChanged(); + selectStep(); }); -function selectComponent() { - it('should disable certain options in the select component', async () => { - setUpThreeComponentsSpy('OpenResponse', 'Graph', 'OpenResponse'); - component.referenceComponent.nodeId = nodeId1; - component.thisComponentId = componentId1; - component.ngOnInit(); - const selects = await loader.getAllHarnesses(MatSelectHarness); - const selectComponent = selects[1]; - await selectComponent.open(); - const options = await selectComponent.getOptions(); - expect(await options[0].isDisabled()).toBeTrue(); - expect(await options[1].isDisabled()).toBeTrue(); - expect(await options[2].isDisabled()).toBeFalse(); - }); -} - -function stepChanged() { - describe('stepChanged()', () => { - it('should handle step changed when there are no allowed components', async () => { - await setComponentsAndCallStepChanged('Draw', 'Graph', 'Table'); - expectReferenceComponentValues(nodeId1, null); +function selectStep() { + describe('selecting a step', () => { + describe('when the step has compnents that can and cannot be selected', () => { + it('disables certain options in the select component', async () => { + setUpThreeComponentsSpy('OpenResponse', 'Graph', 'OpenResponse'); + const selectStepHarness = await harness.getSelectStep(); + const selectStep = await selectStepHarness.getSelect(); + await selectStep.open(); + await selectStep.clickOptions({ text: nodeTitle1 }); + const selectComponentHarness = await harness.getSelectComponent(); + const selectComponent = await selectComponentHarness.getSelect(); + await selectComponent.open(); + const options = await selectComponent.getOptions(); + expect(await options[0].isDisabled()).toBeTrue(); + expect(await options[1].isDisabled()).toBeTrue(); + expect(await options[2].isDisabled()).toBeFalse(); + }); }); - - it('should handle step changed when there is one allowed component', async () => { - await setComponentsAndCallStepChanged('Draw', 'OpenResponse', 'Table'); - expectReferenceComponentValues(nodeId1, componentId2); + describe('when the step has no components that can be selected', () => { + it('does not automatically select a component', async () => { + await setComponentsAndCallStepChanged('Draw', 'Graph', 'Table'); + expectReferenceComponentValues(nodeId1, null); + }); }); - it('should handle step changed when there are multiple allowed components', async () => { - await setComponentsAndCallStepChanged('Draw', 'OpenResponse', 'OpenResponse'); - expectReferenceComponentValues(nodeId1, null); + describe('when the step has one component that can be selected', () => { + it('automatically selects the one allowed component', async () => { + await setComponentsAndCallStepChanged('Draw', 'OpenResponse', 'Table'); + expectReferenceComponentValues(nodeId1, componentId2); + }); + }); + describe('when the step has many components that can be selected', () => { + it('doesn not automatically select a component', async () => { + await setComponentsAndCallStepChanged('Draw', 'OpenResponse', 'OpenResponse'); + expectReferenceComponentValues(nodeId1, null); + }); }); }); } diff --git a/src/app/authoring-tool/select-step-and-component/select-step-and-component.component.ts b/src/app/authoring-tool/select-step-and-component/select-step-and-component.component.ts index d1c117f7646..5c20b27f0ce 100644 --- a/src/app/authoring-tool/select-step-and-component/select-step-and-component.component.ts +++ b/src/app/authoring-tool/select-step-and-component/select-step-and-component.component.ts @@ -1,76 +1,33 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { ProjectService } from '../../../assets/wise5/services/projectService'; +import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core'; import { ReferenceComponent } from '../../domain/referenceComponent'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { FormsModule } from '@angular/forms'; -import { CommonModule } from '@angular/common'; import { SelectStepComponent } from '../select-step/select-step.component'; +import { SelectComponentComponent } from '../select-component/select-component.component'; @Component({ selector: 'select-step-and-component', templateUrl: './select-step-and-component.component.html', styleUrls: ['./select-step-and-component.component.scss'], standalone: true, - imports: [CommonModule, FormsModule, MatFormFieldModule, MatSelectModule, SelectStepComponent] + imports: [SelectComponentComponent, SelectStepComponent] }) -export class SelectStepAndComponentComponent implements OnInit { +export class SelectStepAndComponentComponent { @Input() allowedComponentTypes: string[] = []; @Output() componentChange: EventEmitter = new EventEmitter(); - protected components: any[] = []; - protected componentToIsAllowed: Map = new Map(); @Input() referenceComponent: ReferenceComponent; @Output() stepChange: EventEmitter = new EventEmitter(); @Input() thisComponentId: string; - constructor(private projectService: ProjectService) {} - - ngOnInit(): void { - if (this.referenceComponent.nodeId != null) { - this.calculateComponents(this.referenceComponent.nodeId); - if (this.referenceComponent.componentId == null) { - this.automaticallySetComponentIfPossible(this.referenceComponent.nodeId); - } - } - } - - private calculateComponents(nodeId: string): void { - this.components = this.projectService.getComponents(nodeId); - for (const component of this.components) { - this.componentToIsAllowed.set( - component.id, - this.allowedComponentTypes.includes(component.type) - ); - } - } + constructor(private changeDetector: ChangeDetectorRef) {} protected stepChanged(nodeId: string): void { this.referenceComponent.nodeId = nodeId; - this.referenceComponent.componentId = null; - this.automaticallySetComponentIfPossible(nodeId); - this.calculateComponents(nodeId); + this.changeDetector.detectChanges(); this.stepChange.emit(this.referenceComponent); } - protected componentChanged(): void { + protected componentChanged(componentId: string): void { + this.referenceComponent.componentId = componentId; + this.changeDetector.detectChanges(); this.componentChange.emit(this.referenceComponent); } - - private automaticallySetComponentIfPossible(nodeId: string): void { - let numAllowedComponents = 0; - let allowedComponent = null; - for (const component of this.projectService.getComponents(nodeId)) { - if ( - this.allowedComponentTypes.includes(component.type) && - component.id !== this.thisComponentId - ) { - numAllowedComponents += 1; - allowedComponent = component; - } - } - if (numAllowedComponents === 1) { - this.referenceComponent.componentId = allowedComponent.id; - this.componentChanged(); - } - } } diff --git a/src/app/authoring-tool/select-step-and-component/select-step-and-component.harness.ts b/src/app/authoring-tool/select-step-and-component/select-step-and-component.harness.ts index e38ddaf87c8..18ccd5a845d 100644 --- a/src/app/authoring-tool/select-step-and-component/select-step-and-component.harness.ts +++ b/src/app/authoring-tool/select-step-and-component/select-step-and-component.harness.ts @@ -1,7 +1,9 @@ import { ComponentHarness } from '@angular/cdk/testing'; import { SelectStepHarness } from '../select-step/select-step.harness'; +import { SelectComponentHarness } from '../select-component/select-component.harness'; export class SelectStepAndComponentHarness extends ComponentHarness { static hostSelector = 'select-step-and-component'; + getSelectComponent = this.locatorFor(SelectComponentHarness); getSelectStep = this.locatorFor(SelectStepHarness); } diff --git a/src/messages.xlf b/src/messages.xlf index aed0c63c174..13be9c72eba 100644 --- a/src/messages.xlf +++ b/src/messages.xlf @@ -1641,8 +1641,8 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. Component - src/app/authoring-tool/select-step-and-component/select-step-and-component.component.html - 7 + src/app/authoring-tool/select-component/select-component.component.html + 2 src/assets/wise5/components/animation/animation-authoring/animation-authoring.component.html @@ -1660,8 +1660,8 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. (This Component) - src/app/authoring-tool/select-step-and-component/select-step-and-component.component.html - 15 + src/app/authoring-tool/select-component/select-component.component.html + 10 src/assets/wise5/components/openResponse/edit-open-response-advanced/edit-open-response-advanced.component.html