lp1754364: example solution user/sandbergja/lp1754364-example-solution
authorJane Sandberg <js7389@princeton.edu>
Sat, 6 May 2023 15:08:20 +0000 (08:08 -0700)
committerJane Sandberg <js7389@princeton.edu>
Sun, 7 May 2023 00:46:35 +0000 (17:46 -0700)
Signed-off-by: Jane Sandberg <js7389@princeton.edu>
Open-ILS/src/eg2/src/app/share/dialog/dialog.component.ts
Open-ILS/src/eg2/src/app/share/util/timezone.service.spec.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/util/timezone.service.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/edit-org-unit-setting-dialog.component.html
Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/edit-org-unit-setting-dialog.component.spec.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/edit-org-unit-setting-dialog.component.ts
Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/org-unit-settings.module.ts
Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/timezone-select/timezone-select.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/timezone-select/timezone-select.component.spec.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/timezone-select/timezone-select.component.ts [new file with mode: 0644]

index 6f73bd5..f8be661 100644 (file)
@@ -44,8 +44,7 @@ export class DialogComponent implements OnInit {
     @Input() public dialogTitle: string;
 
     // Pointer to the dialog content template.
-    @ViewChild('dialogContent', {static: false})
-    private dialogContent: TemplateRef<any>;
+    @ViewChild('dialogContent', {static: false}) dialogContent: TemplateRef<any>;
 
     identifier: number = DialogComponent.counter++;
 
diff --git a/Open-ILS/src/eg2/src/app/share/util/timezone.service.spec.ts b/Open-ILS/src/eg2/src/app/share/util/timezone.service.spec.ts
new file mode 100644 (file)
index 0000000..4b08dbc
--- /dev/null
@@ -0,0 +1,13 @@
+import { TimezoneService } from "./timezone.service";
+
+describe('TimezoneService', () => {
+    describe('#validTimezones', () => {
+        it('includes valid timezones', () => {
+            expect(new TimezoneService().validTimezones()).toContain('America/Vancouver')
+        });
+        it('does not include duplicate timezones', () => {
+            const timezones = new TimezoneService().validTimezones();
+            expect(timezones.length).toEqual(new Set(timezones).size)
+        });
+    });
+});
diff --git a/Open-ILS/src/eg2/src/app/share/util/timezone.service.ts b/Open-ILS/src/eg2/src/app/share/util/timezone.service.ts
new file mode 100644 (file)
index 0000000..a947ce1
--- /dev/null
@@ -0,0 +1,7 @@
+import * as moment from 'moment-timezone';
+
+export class TimezoneService {
+    validTimezones() {
+        return moment.tz.names();
+    }
+}
index b929ac8..5dda1b4 100644 (file)
                       </ng-container><!-- fmClass ngSwitch -->
                 </div>
               </ng-container>
+              <ng-container *ngSwitchCase="'timezone'">
+                <div class="input-group">
+                    <eg-timezone-select
+                      [(ngModel)]="entryValue">
+                    </eg-timezone-select>
+                </div>
+              </ng-container>
 
             </ng-container> <!-- input type ngSwitch -->
         </div>
       (click)="delete()" i18n>Delete Setting</button>
   </div>
 </ng-template>
-
 <ng-template #fmClassLabel let-r="result" i18n>
   {{r.label}}
 </ng-template>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/edit-org-unit-setting-dialog.component.spec.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/edit-org-unit-setting-dialog.component.spec.ts
new file mode 100644 (file)
index 0000000..e959cb3
--- /dev/null
@@ -0,0 +1,76 @@
+import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
+import { EditOuSettingDialogComponent } from "./edit-org-unit-setting-dialog.component";
+import { TestBed, waitForAsync } from "@angular/core/testing";
+import { AfterViewInit, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectorRef, Component, TemplateRef, ViewChild } from "@angular/core";
+
+const modal = jasmine.createSpyObj<NgbModal>(['open']);
+let component = new EditOuSettingDialogComponent(modal);
+
+@Component({
+    template: `
+      <div>
+        <ng-container *ngTemplateOutlet="modal"> </ng-container>
+      </div>
+      <eg-admin-edit-org-unit-setting-dialog #dialog></eg-admin-edit-org-unit-setting-dialog>
+    `,
+  })
+  class MockModalComponent implements AfterViewInit {
+    @ViewChild('dialog') componentRef: EditOuSettingDialogComponent;
+    modal: TemplateRef<any>;
+    constructor(private cdr: ChangeDetectorRef) {}
+    ngAfterViewInit() {
+      this.modal = this.componentRef.dialogContent;
+      this.cdr.detectChanges();
+    }
+  }
+
+describe('EditOuSettingDialogComponent', () => {
+    describe('inputType()', () => {
+        describe('when setting name is lib.timezone', () => {
+            it('returns timezone', () => {
+                const entry = {
+                    name: 'lib.timezone',
+                    dataType: 'string'
+                };
+                component.entry = entry;
+                expect(component.inputType()).toEqual('timezone');
+            });
+        });
+        describe('when setting dataType is integer', () => {
+            it('returns integer', () => {
+                const entry = {
+                    dataType: 'integer'
+                };
+                component.entry = entry;
+                expect(component.inputType()).toEqual('integer');
+            });
+        });
+    });
+    describe('template', () => {
+        it(('displays a timezone select if the entry is lib.timezone'), waitForAsync(() => {
+            TestBed.configureTestingModule({
+                providers: [
+                    { provide: NgbModal, useValue: modal}
+                ], declarations: [
+                    MockModalComponent,
+                    EditOuSettingDialogComponent
+                ], schemas: [
+                    CUSTOM_ELEMENTS_SCHEMA
+                ]
+            }).compileComponents();
+            const fixture = TestBed.createComponent(MockModalComponent);
+            const mockModal = fixture.debugElement.componentInstance;
+            fixture.detectChanges();
+            mockModal.ngAfterViewInit();
+            let component = mockModal.componentRef;
+            const entry = {
+                name: 'lib.timezone',
+                dataType: 'string'
+            };
+            component.entry = entry;
+            const editElement: HTMLElement = fixture.nativeElement;
+            fixture.detectChanges();
+            expect(editElement.querySelectorAll('eg-timezone-select').length).toEqual(1);
+        }))
+    });
+});
index 13ef179..0dd43bb 100644 (file)
@@ -28,6 +28,9 @@ export class EditOuSettingDialogComponent extends DialogComponent {
     }
 
     inputType() {
+        if (this.entry.name === 'lib.timezone') {
+            return 'timezone';
+        }
         return this.entry.dataType;
     }
 
index 35a4a0d..bdb0ab3 100644 (file)
@@ -7,13 +7,16 @@ import {OuSettingHistoryDialogComponent} from './org-unit-setting-history-dialog
 import {OrgUnitSettingsRoutingModule} from './org-unit-settings-routing.module';
 import {OuSettingJsonDialogComponent} from './org-unit-setting-json-dialog.component';
 import {ItemLocationSelectModule} from '@eg/share/item-location-select/item-location-select.module';
+import { TimezoneSelectComponent } from './timezone-select/timezone-select.component';
+import { TimezoneService } from '@eg/share/util/timezone.service';
 
 @NgModule({
     declarations: [
         OrgUnitSettingsComponent,
         EditOuSettingDialogComponent,
         OuSettingHistoryDialogComponent,
-        OuSettingJsonDialogComponent
+        OuSettingJsonDialogComponent,
+        TimezoneSelectComponent
     ],
     imports: [
         AdminCommonModule,
@@ -24,6 +27,7 @@ import {ItemLocationSelectModule} from '@eg/share/item-location-select/item-loca
     exports: [
     ],
     providers: [
+        TimezoneService
     ]
 })
 
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/timezone-select/timezone-select.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/timezone-select/timezone-select.component.html
new file mode 100644 (file)
index 0000000..251f5de
--- /dev/null
@@ -0,0 +1,7 @@
+<label for="timezone-select" i18n>Timezone</label>
+<eg-combobox domId="timezone-select"
+             [startId]="startId"
+             [entries]="entries"
+             (onChange)="cboxChanged($event)"
+             #combobox>
+</eg-combobox>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/timezone-select/timezone-select.component.spec.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/timezone-select/timezone-select.component.spec.ts
new file mode 100644 (file)
index 0000000..39cef0c
--- /dev/null
@@ -0,0 +1,11 @@
+import { TimezoneService } from '@eg/share/util/timezone.service';
+import { TimezoneSelectComponent } from './timezone-select.component';
+
+describe('TimezoneSelectComponent', () => {
+  it('should have an entry for each valid timezone', () => {
+    const service = jasmine.createSpyObj<TimezoneService>(['validTimezones']);
+    service.validTimezones.and.returnValue(['America/Vancouver']);
+    const component = new TimezoneSelectComponent(service);
+    expect(component.entries).toContain({id: 'America/Vancouver', label: 'America/Vancouver'});
+  })
+});
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/timezone-select/timezone-select.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/org-unit-settings/timezone-select/timezone-select.component.ts
new file mode 100644 (file)
index 0000000..1b4ad70
--- /dev/null
@@ -0,0 +1,51 @@
+import { Component, ViewChild, forwardRef } from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+import { ComboboxComponent, ComboboxEntry } from '@eg/share/combobox/combobox.component';
+import { TimezoneService } from '@eg/share/util/timezone.service';
+
+@Component({
+  selector: 'eg-timezone-select',
+  templateUrl: './timezone-select.component.html',
+  providers: [{
+    provide: NG_VALUE_ACCESSOR,
+    useExisting: forwardRef(() => TimezoneSelectComponent),
+    multi: true
+  }]
+})
+export class TimezoneSelectComponent implements ControlValueAccessor {
+  entries: ComboboxEntry[];
+  startId: string;
+
+  constructor(
+    private timezone: TimezoneService
+  ) {
+    this.entries = this.timezone.validTimezones().map((timezone) => {
+      return {id: timezone, label: timezone}
+    });
+  }
+
+  @ViewChild('combobox') combobox: ComboboxComponent;
+
+  writeValue(id: string): void {
+    if (this.combobox) {
+      this.combobox.selectedId = id;
+    } else {
+      // Too early in the lifecycle
+      this.startId = id;
+    }
+  }
+
+  cboxChanged(entry: ComboboxEntry) {
+    this.propagateChange(entry?.id);
+}
+
+  // Stub functions required by ControlValueAccessor
+  propagateChange = (_: any) => {};
+  propagateTouch = () => {};
+  registerOnChange(fn) {
+    this.propagateChange = fn;
+  }
+  registerOnTouched(fn) {
+      this.propagateTouch = fn;
+  }
+}