[open-ils-commits] [GIT] Evergreen ILS branch master updated. c20fa9d5c4b8f7dcf4cd7312c5ebcdf04c1a44e2

Evergreen Git git at git.evergreen-ils.org
Wed Jul 10 14:13:08 EDT 2019


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, master has been updated
       via  c20fa9d5c4b8f7dcf4cd7312c5ebcdf04c1a44e2 (commit)
       via  7ae4cbb61c2fea98f36da8f4096ffb279ccb5c2d (commit)
       via  4cd71ffef83d438c842ad4fea4c522da9b139fea (commit)
       via  00854e37785858c0b187ee6f74f6ceeb7792f46d (commit)
       via  a4490662f5f81b11bb6a38507dc8eae2d669a8b9 (commit)
      from  1fe8de72aee2c77d831906985772637a8298713e (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit c20fa9d5c4b8f7dcf4cd7312c5ebcdf04c1a44e2
Author: Bill Erickson <berickxx at gmail.com>
Date:   Tue Jul 9 11:46:02 2019 -0400

    LP1830432 Org family renders checkboxes horizontally
    
    Consistent with the original layout of the org-select + checkboxes,
    render the org family selector with checkboxes stacked vertically
    along the right of the selector instead of below it.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Jane Sandberg <sandbej at linnbenton.edu>

diff --git a/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.html b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.html
index b9bdf0281c..5ffaf5a03d 100644
--- a/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.html
+++ b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.html
@@ -1,26 +1,31 @@
-<div class="input-group">
-  <div class="input-group-prepend">
-    <span class="input-group-text">{{labelText}}</span>
+<form class="form-inline" [formGroup]="familySelectors">
+  <div class="input-group">
+    <div class="input-group-prepend">
+      <span class="input-group-text">{{labelText}}</span>
+    </div>
+    <eg-org-select [domId]="domId"
+      (onChange)="orgOnChange($event)"
+      [limitPerms]="limitPerms"
+      [initialOrgId]="selectedOrgId">
+    </eg-org-select>
   </div>
-  <eg-org-select [domId]="domId"
-    (onChange)="orgOnChange($event)"
-    [limitPerms]="limitPerms"
-    [initialOrgId]="selectedOrgId">
-  </eg-org-select>
-</div>
-<form class="pl-2" [formGroup]="familySelectors">
-  <div class="form-check" *ngIf="!hideAncestorSelector">
-    <input type="checkbox"
-      formControlName="includeAncestors"
-      (blur)="propagateTouch()"
-      class="form-check-input" id="{{domId}}-include-ancestors">
-    <label class="form-check-label" for="{{domId}}-include-ancestors" i18n>+ Ancestors</label>
-  </div>
-  <div class="form-check" *ngIf="!hideDescendantSelector">
-    <input type="checkbox"
-      formControlName="includeDescendants"
-      (blur)="propagateTouch()"
-      class="form-check-input" id="{{domId}}-include-descendants">
-    <label class="form-check-label" for="{{domId}}-include-descendants" i18n>+ Descendants</label>
+
+  <!-- stack the checkboxes -->
+  <div class="ml-2 d-flex flex-column align-items-start">
+    <div class="form-check" *ngIf="!hideAncestorSelector">
+      <input type="checkbox"
+        formControlName="includeAncestors"
+        (blur)="propagateTouch()"
+        class="form-check-input" id="{{domId}}-include-ancestors">
+      <label class="form-check-label" for="{{domId}}-include-ancestors" i18n>+ Ancestors</label>
+    </div>
+    <div class="form-check" *ngIf="!hideDescendantSelector">
+      <input type="checkbox"
+        formControlName="includeDescendants"
+        (blur)="propagateTouch()"
+        class="form-check-input" id="{{domId}}-include-descendants">
+      <label class="form-check-label" for="{{domId}}-include-descendants" i18n>+ Descendants</label>
+    </div>
   </div>
 </form>
+

commit 7ae4cbb61c2fea98f36da8f4096ffb279ccb5c2d
Author: Jane Sandberg <sandbej at linnbenton.edu>
Date:   Mon Jul 8 07:03:50 2019 -0700

    LP1830432: Use a stub callback with registerOnTouched
    
    Signed-off-by: Jane Sandberg <sandbej at linnbenton.edu>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.html b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.html
index 31347512a8..b9bdf0281c 100644
--- a/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.html
+++ b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.html
@@ -12,14 +12,14 @@
   <div class="form-check" *ngIf="!hideAncestorSelector">
     <input type="checkbox"
       formControlName="includeAncestors"
-      (blur)="registerOnTouched()"
+      (blur)="propagateTouch()"
       class="form-check-input" id="{{domId}}-include-ancestors">
     <label class="form-check-label" for="{{domId}}-include-ancestors" i18n>+ Ancestors</label>
   </div>
   <div class="form-check" *ngIf="!hideDescendantSelector">
     <input type="checkbox"
       formControlName="includeDescendants"
-      (blur)="registerOnTouched()"
+      (blur)="propagateTouch()"
       class="form-check-input" id="{{domId}}-include-descendants">
     <label class="form-check-label" for="{{domId}}-include-descendants" i18n>+ Descendants</label>
   </div>
diff --git a/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.ts b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.ts
index 3e1e6e8e34..bf10232712 100644
--- a/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.ts
+++ b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.ts
@@ -61,6 +61,7 @@ export class OrgFamilySelectComponent implements ControlValueAccessor, OnInit {
     familySelectors: FormGroup;
 
     propagateChange = (_: OrgFamily) => {};
+    propagateTouch = () => {};
 
     constructor(
         private auth: AuthService,
@@ -138,7 +139,9 @@ export class OrgFamilySelectComponent implements ControlValueAccessor, OnInit {
         this.propagateChange = fn;
     }
 
-    registerOnTouched() {}
+    registerOnTouched(fn) {
+        this.propagateTouch = fn;
+    }
 
     disableAncestorSelector(): boolean {
         return this.options.primaryOrgId === this.org.root().id();

commit 4cd71ffef83d438c842ad4fea4c522da9b139fea
Author: Bill Erickson <berickxx at gmail.com>
Date:   Fri Jul 5 11:18:59 2019 -0400

    LP1830432 Uniqify reported org IDs / sandbox tweaks
    
    Err on the side of caution and ensure the org family selector always
    reports a unique list of org unit IDs.
    
    Sandbox language tweaks.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    Signed-off-by: Jane Sandberg <sandbej at linnbenton.edu>

diff --git a/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.ts b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.ts
index 6fd1790c98..3e1e6e8e34 100644
--- a/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.ts
+++ b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.ts
@@ -110,14 +110,18 @@ export class OrgFamilySelectComponent implements ControlValueAccessor, OnInit {
             }
 
             if (this.includeDescendants.value) {
-                // can result in duplicate workstation org IDs... meh
                 this.options.orgIds = this.options.orgIds.concat(
                     this.org.descendants(this.options.primaryOrgId, true));
             }
 
+            // Using ancestors() and descendants() can result in
+            // duplicate org ID's.  Be nice and uniqify.
+            const hash: any = {};
+            this.options.orgIds.forEach(id => hash[id] = true);
+            this.options.orgIds = Object.keys(hash).map(id => Number(id));
+
             this.propagateChange(this.options);
         };
-
     }
 
     writeValue(value: OrgFamily) {
diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html
index fa58e905e0..38908aeed6 100644
--- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html
@@ -200,11 +200,11 @@
       <div class="card-text">
         <eg-org-family-select
           formControlName="badOrgSelector"
-          labelText="Choose the worst libraries">
+          labelText="Choose the fanciest libraries">
         </eg-org-family-select>
-	<div *ngIf="!badOrgForm.valid" class="alert alert-danger">
+        <div *ngIf="!badOrgForm.valid" class="alert alert-danger">
           <span class="material-icons">error</span>
-          <span i18n>Too many bad libraries!</span>
+          <span i18n>Too many fancy libraries!</span>
         </div>
       </div>
     </div>
diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts
index 17c6e6deee..52931a04e4 100644
--- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts
@@ -94,7 +94,7 @@ export class SandboxComponent implements OnInit {
                 {'id': 4, 'includeAncestors': false, 'includeDescendants': true}, (c: FormControl) => {
                     // An Angular custom validator
                     if (c.value.orgIds && c.value.orgIds.length > 5) {
-                        return { tooMany: 'That\'s too many bad libraries!' };
+                        return { tooMany: 'That\'s too many fancy libraries!' };
                     } else {
                         return null;
                     }
@@ -102,7 +102,7 @@ export class SandboxComponent implements OnInit {
         });
 
         this.badOrgForm.get('badOrgSelector').valueChanges.subscribe(bad => {
-            this.toast.danger('The worst libraries are: ' + JSON.stringify(bad.orgIds));
+            this.toast.danger('The fanciest libraries are: ' + JSON.stringify(bad.orgIds));
         });
 
         this.gridDataSource.data = [

commit 00854e37785858c0b187ee6f74f6ceeb7792f46d
Author: Jane Sandberg <sandbej at linnbenton.edu>
Date:   Wed Jul 3 09:46:04 2019 -0700

    LP1830432: Make sure that unit tests have an org unit selected
    
    Signed-off-by: Jane Sandberg <sandbej at linnbenton.edu>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.spec.ts b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.spec.ts
index 3e7117cb31..f93b386216 100644
--- a/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.spec.ts
+++ b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.spec.ts
@@ -26,9 +26,8 @@ describe('Component: OrgFamilySelect', () => {
 
     beforeEach(() => {
         // stub of OrgService for testing
-        // with a very simple org structure:
-        // 1 is the root note
-        // 2 is its child
+        // with a super simple org structure:
+        // 1 is the root note, with no children
         orgServiceStub = {
             root: () => {
                 return {
@@ -42,7 +41,7 @@ describe('Component: OrgFamilySelect', () => {
                     a: [],
                     classname: 'aou',
                     _isfieldmapper: true,
-                    children: () => Array(2 - ouId) };
+                    children: () => Array() };
             }
         };
         cookieServiceStub = {};
@@ -59,6 +58,7 @@ describe('Component: OrgFamilySelect', () => {
         fixture = TestBed.createComponent(OrgFamilySelectComponent);
         component = fixture.componentInstance;
         component.domId = 'family-test';
+        component.selectedOrgId = 1;
         fixture.detectChanges();
     });
 
@@ -97,12 +97,19 @@ describe('Component: OrgFamilySelect', () => {
 
     it('disables includeAncestors checkbox when root OU is chosen', () => {
         fixture.whenStable().then(() => {
-            component.selectedOrgId = 1;
             fixture.detectChanges();
             includeAncestors = fixture.debugElement.query(By.css('#family-test-include-ancestors'));
             expect(includeAncestors.nativeElement.disabled).toBe(true);
         });
     });
 
+    it('disables includeAncestors checkbox when OU has no children', () => {
+        fixture.whenStable().then(() => {
+            fixture.detectChanges();
+            includeDescendants = fixture.debugElement.query(By.css('#family-test-include-descendants'));
+            expect(includeDescendants.nativeElement.disabled).toBe(true);
+        });
+    });
+
 });
 

commit a4490662f5f81b11bb6a38507dc8eae2d669a8b9
Author: Jane Sandberg <sandbej at linnbenton.edu>
Date:   Sun Jun 23 10:22:20 2019 -0700

    LP1830432: Make the org-family-select reusable
    
    This commit removes Bill Erickson's automagic org unit select with
    +Ancestors and +Descendants checkboxes from the admin-page component,
    and gives it a component of its own, called <eg-org-family-select>.
    
    This commit also makes it compatible with [(ngModel)], reactive forms,
    and any custom Angular validators you might want to throw at it.
    Examples of all three are available in the sandbox.
    
    Also includes some component tests.
    
    Signed-off-by: Jane Sandberg <sandbej at linnbenton.edu>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.html b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.html
new file mode 100644
index 0000000000..31347512a8
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.html
@@ -0,0 +1,26 @@
+<div class="input-group">
+  <div class="input-group-prepend">
+    <span class="input-group-text">{{labelText}}</span>
+  </div>
+  <eg-org-select [domId]="domId"
+    (onChange)="orgOnChange($event)"
+    [limitPerms]="limitPerms"
+    [initialOrgId]="selectedOrgId">
+  </eg-org-select>
+</div>
+<form class="pl-2" [formGroup]="familySelectors">
+  <div class="form-check" *ngIf="!hideAncestorSelector">
+    <input type="checkbox"
+      formControlName="includeAncestors"
+      (blur)="registerOnTouched()"
+      class="form-check-input" id="{{domId}}-include-ancestors">
+    <label class="form-check-label" for="{{domId}}-include-ancestors" i18n>+ Ancestors</label>
+  </div>
+  <div class="form-check" *ngIf="!hideDescendantSelector">
+    <input type="checkbox"
+      formControlName="includeDescendants"
+      (blur)="registerOnTouched()"
+      class="form-check-input" id="{{domId}}-include-descendants">
+    <label class="form-check-label" for="{{domId}}-include-descendants" i18n>+ Descendants</label>
+  </div>
+</form>
diff --git a/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.spec.ts b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.spec.ts
new file mode 100644
index 0000000000..3e7117cb31
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.spec.ts
@@ -0,0 +1,108 @@
+import {ComponentFixture, TestBed} from '@angular/core/testing';
+import {Component, DebugElement, Input} from '@angular/core';
+import {By} from '@angular/platform-browser';
+import {OrgFamilySelectComponent} from './org-family-select.component';
+import {ReactiveFormsModule} from '@angular/forms';
+import {CookieService} from 'ngx-cookie';
+import {OrgService} from '@eg/core/org.service';
+
+ at Component({
+    selector: 'eg-org-select',
+    template: ''
+})
+class MockOrgSelectComponent {
+    @Input() domId: string;
+    @Input() limitPerms: string;
+    @Input() initialOrgId: number;
+}
+
+describe('Component: OrgFamilySelect', () => {
+    let component: OrgFamilySelectComponent;
+    let fixture: ComponentFixture<OrgFamilySelectComponent>;
+    let includeAncestors: DebugElement;
+    let includeDescendants: DebugElement;
+    let orgServiceStub: Partial<OrgService>;
+    let cookieServiceStub: Partial<CookieService>;
+
+    beforeEach(() => {
+        // stub of OrgService for testing
+        // with a very simple org structure:
+        // 1 is the root note
+        // 2 is its child
+        orgServiceStub = {
+            root: () => {
+                return {
+                    a: [],
+                    classname: 'aou',
+                    _isfieldmapper: true,
+                    id: () => 1};
+            },
+            get: (ouId: number) => {
+                return {
+                    a: [],
+                    classname: 'aou',
+                    _isfieldmapper: true,
+                    children: () => Array(2 - ouId) };
+            }
+        };
+        cookieServiceStub = {};
+        TestBed.configureTestingModule({
+            imports: [
+                ReactiveFormsModule,
+            ], providers: [
+                { provide: CookieService, useValue: cookieServiceStub },
+                { provide: OrgService, useValue: orgServiceStub},
+            ], declarations: [
+                OrgFamilySelectComponent,
+                MockOrgSelectComponent,
+        ]});
+        fixture = TestBed.createComponent(OrgFamilySelectComponent);
+        component = fixture.componentInstance;
+        component.domId = 'family-test';
+        fixture.detectChanges();
+    });
+
+
+    it('provides includeAncestors checkbox by default', () => {
+        fixture.whenStable().then(() => {
+            includeAncestors = fixture.debugElement.query(By.css('#family-test-include-ancestors'));
+            expect(includeAncestors.nativeElement).toBeTruthy();
+        });
+    });
+
+    it('provides includeDescendants checkbox by default', () => {
+        fixture.whenStable().then(() => {
+            includeDescendants = fixture.debugElement.query(By.css('#family-test-include-descendants'));
+            expect(includeDescendants.nativeElement).toBeTruthy();
+        });
+    });
+
+    it('allows user to turn off includeAncestors checkbox', () => {
+        fixture.whenStable().then(() => {
+            component.hideAncestorSelector = true;
+            fixture.detectChanges();
+            includeAncestors = fixture.debugElement.query(By.css('#family-test-include-ancestors'));
+            expect(includeAncestors).toBeNull();
+        });
+    });
+
+    it('allows user to turn off includeDescendants checkbox', () => {
+        fixture.whenStable().then(() => {
+            component.hideDescendantSelector = true;
+            fixture.detectChanges();
+            includeDescendants = fixture.debugElement.query(By.css('#family-test-include-descendants'));
+            expect(includeDescendants).toBeNull();
+        });
+    });
+
+    it('disables includeAncestors checkbox when root OU is chosen', () => {
+        fixture.whenStable().then(() => {
+            component.selectedOrgId = 1;
+            fixture.detectChanges();
+            includeAncestors = fixture.debugElement.query(By.css('#family-test-include-ancestors'));
+            expect(includeAncestors.nativeElement.disabled).toBe(true);
+        });
+    });
+
+});
+
diff --git a/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.ts b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.ts
new file mode 100644
index 0000000000..6fd1790c98
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.ts
@@ -0,0 +1,156 @@
+import {Component, EventEmitter, OnInit, Input, Output, ViewChild, forwardRef} from '@angular/core';
+import {ControlValueAccessor, FormGroup, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
+import {AuthService} from '@eg/core/auth.service';
+import {IdlObject} from '@eg/core/idl.service';
+import {OrgService} from '@eg/core/org.service';
+
+export interface OrgFamily {
+  primaryOrgId: number;
+  includeAncestors?: boolean;
+  includeDescendants?: boolean;
+  orgIds?: number[];
+}
+
+ at Component({
+    selector: 'eg-org-family-select',
+    templateUrl: 'org-family-select.component.html',
+    providers: [
+    {
+      provide: NG_VALUE_ACCESSOR,
+      useExisting: forwardRef(() => OrgFamilySelectComponent),
+      multi: true
+    }
+  ]
+})
+export class OrgFamilySelectComponent implements ControlValueAccessor, OnInit {
+
+    // The label for this input
+    @Input() labelText = 'Library';
+
+    // Should the Ancestors checkbox be hidden?
+    @Input() hideAncestorSelector = false;
+
+    // Should the Descendants checkbox be hidden?
+    @Input() hideDescendantSelector = false;
+
+    // Should the Ancestors checkbox be checked by default?
+    //
+    // Ignored if [hideAncestorSelector]="true"
+    @Input() ancestorSelectorChecked = false;
+
+    // Should the Descendants checkbox be checked by default?
+    //
+    // Ignored if [hideDescendantSelector]="true"
+    @Input() descendantSelectorChecked = false;
+
+    // Default org unit
+    @Input() selectedOrgId: number;
+
+    // Only show the OUs that the user has certain permissions at
+    @Input() limitPerms: string[];
+
+    @Input() domId: string;
+
+    // this is the most up-to-date value used for ngModel and reactive form
+    // subscriptions
+    options: OrgFamily;
+
+    orgOnChange: ($event: IdlObject) => void;
+    emitArray: () => void;
+
+    familySelectors: FormGroup;
+
+    propagateChange = (_: OrgFamily) => {};
+
+    constructor(
+        private auth: AuthService,
+        private org: OrgService
+    ) {
+    }
+
+    ngOnInit() {
+        if (this.selectedOrgId) {
+            this.options = {primaryOrgId: this.selectedOrgId};
+        } else if (this.auth.user()) {
+            this.options = {primaryOrgId: this.auth.user().ws_ou()};
+        }
+
+        this.familySelectors = new FormGroup({
+            'includeAncestors': new FormControl({
+                value: this.ancestorSelectorChecked,
+                disabled: this.disableAncestorSelector()}),
+            'includeDescendants': new FormControl({
+                value: this.descendantSelectorChecked,
+                disabled: this.disableDescendantSelector()}),
+        });
+
+        if (!this.domId) {
+            this.domId = 'org-family-select-' + Math.floor(Math.random() * 100000);
+        }
+
+        this.familySelectors.valueChanges.subscribe(val => {
+            this.emitArray();
+        });
+
+        this.orgOnChange = ($event: IdlObject) => {
+            this.options.primaryOrgId = $event.id();
+            this.disableAncestorSelector() ? this.includeAncestors.disable() : this.includeAncestors.enable();
+            this.disableDescendantSelector() ? this.includeDescendants.disable() : this.includeDescendants.enable();
+            this.emitArray();
+        };
+
+        this.emitArray = () => {
+            // Prepare and emit an array containing the primary org id and
+            // optionally ancestor and descendant org units.
+
+            this.options.orgIds = [this.options.primaryOrgId];
+
+            if (this.includeAncestors.value) {
+                this.options.orgIds = this.org.ancestors(this.options.primaryOrgId, true);
+            }
+
+            if (this.includeDescendants.value) {
+                // can result in duplicate workstation org IDs... meh
+                this.options.orgIds = this.options.orgIds.concat(
+                    this.org.descendants(this.options.primaryOrgId, true));
+            }
+
+            this.propagateChange(this.options);
+        };
+
+    }
+
+    writeValue(value: OrgFamily) {
+        if (value) {
+            this.selectedOrgId = value['primaryOrgId'];
+            this.familySelectors.patchValue({
+                'includeAncestors': value['includeAncestors'] ? value['includeAncestors'] : false,
+                'includeDescendants': value['includeDescendants'] ? value['includeDescendants'] : false,
+            });
+        }
+    }
+
+    registerOnChange(fn) {
+        this.propagateChange = fn;
+    }
+
+    registerOnTouched() {}
+
+    disableAncestorSelector(): boolean {
+        return this.options.primaryOrgId === this.org.root().id();
+    }
+
+    disableDescendantSelector(): boolean {
+        const contextOrg = this.org.get(this.options.primaryOrgId);
+        return contextOrg.children().length === 0;
+    }
+
+    get includeAncestors() {
+        return this.familySelectors.get('includeAncestors');
+    }
+    get includeDescendants() {
+        return this.familySelectors.get('includeDescendants');
+    }
+
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/common.module.ts b/Open-ILS/src/eg2/src/app/staff/common.module.ts
index 5575b70c3a..bbf959c98c 100644
--- a/Open-ILS/src/eg2/src/app/staff/common.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/common.module.ts
@@ -6,6 +6,7 @@ import {StaffBannerComponent} from './share/staff-banner.component';
 import {ComboboxComponent} from '@eg/share/combobox/combobox.component';
 import {ComboboxEntryComponent} from '@eg/share/combobox/combobox-entry.component';
 import {OrgSelectComponent} from '@eg/share/org-select/org-select.component';
+import {OrgFamilySelectComponent} from '@eg/share/org-family-select/org-family-select.component';
 import {AccessKeyDirective} from '@eg/share/accesskey/accesskey.directive';
 import {AccessKeyService} from '@eg/share/accesskey/accesskey.service';
 import {AccessKeyInfoComponent} from '@eg/share/accesskey/accesskey-info.component';
@@ -22,6 +23,7 @@ import {BibSummaryComponent} from '@eg/staff/share/bib-summary/bib-summary.compo
 import {TranslateComponent} from '@eg/staff/share/translate/translate.component';
 import {AdminPageComponent} from '@eg/staff/share/admin-page/admin-page.component';
 import {EgHelpPopoverComponent} from '@eg/share/eg-help-popover/eg-help-popover.component';
+import {ReactiveFormsModule} from '@angular/forms';
 
 /**
  * Imports the EG common modules and adds modules common to all staff UI's.
@@ -33,6 +35,7 @@ import {EgHelpPopoverComponent} from '@eg/share/eg-help-popover/eg-help-popover.
     ComboboxComponent,
     ComboboxEntryComponent,
     OrgSelectComponent,
+    OrgFamilySelectComponent,
     AccessKeyDirective,
     AccessKeyInfoComponent,
     ToastComponent,
@@ -49,7 +52,8 @@ import {EgHelpPopoverComponent} from '@eg/share/eg-help-popover/eg-help-popover.
   ],
   imports: [
     EgCommonModule,
-    GridModule
+    GridModule,
+    ReactiveFormsModule
   ],
   exports: [
     EgCommonModule,
@@ -58,6 +62,7 @@ import {EgHelpPopoverComponent} from '@eg/share/eg-help-popover/eg-help-popover.
     ComboboxComponent,
     ComboboxEntryComponent,
     OrgSelectComponent,
+    OrgFamilySelectComponent,
     AccessKeyDirective,
     AccessKeyInfoComponent,
     ToastComponent,
diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html
index 9087d044f6..fa58e905e0 100644
--- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html
@@ -178,3 +178,35 @@
 <h4>PCRUD auto flesh and FormatService detection</h4>
 <div *ngIf="aMetarecord">Fingerprint: {{aMetarecord}}</div>
 
+<div class="row">
+  <div class="card col-md-6">
+    <div class="card-body">
+      <h3 class="card-title">Do you like template-driven forms?</h3>
+      <div class="card-text">
+        <eg-org-family-select
+          [ancestorSelectorChecked]="true"
+          [hideDescendantSelector]="true"
+          selectedOrgId="7"
+          labelText="Choose the best libraries"
+          ngModel #bestOnes="ngModel">
+        </eg-org-family-select>
+        The best libraries are: {{bestOnes.value | json}}
+      </div>
+    </div>
+  </div>
+  <form class="card col-md-6" [formGroup]="badOrgForm">
+    <div class="card-body">
+      <h3 class="card-title">Or perhaps reactive forms interest you?</h3>
+      <div class="card-text">
+        <eg-org-family-select
+          formControlName="badOrgSelector"
+          labelText="Choose the worst libraries">
+        </eg-org-family-select>
+	<div *ngIf="!badOrgForm.valid" class="alert alert-danger">
+          <span class="material-icons">error</span>
+          <span i18n>Too many bad libraries!</span>
+        </div>
+      </div>
+    </div>
+  </form>
+</div>
diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts
index de94b5ef74..17c6e6deee 100644
--- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts
@@ -15,6 +15,7 @@ import {PrintService} from '@eg/share/print/print.service';
 import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
 import {FormatService} from '@eg/core/format.service';
 import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
+import {FormGroup, FormControl} from '@angular/forms';
 
 @Component({
   templateUrl: 'sandbox.component.html'
@@ -60,6 +61,8 @@ export class SandboxComponent implements OnInit {
 
     dynamicTitleText: string;
 
+    badOrgForm: FormGroup;
+
     complimentEvergreen: (rows: IdlObject[]) => void;
     notOneSelectedRow: (rows: IdlObject[]) => boolean;
 
@@ -86,6 +89,21 @@ export class SandboxComponent implements OnInit {
     }
 
     ngOnInit() {
+        this.badOrgForm = new FormGroup({
+            'badOrgSelector': new FormControl(
+                {'id': 4, 'includeAncestors': false, 'includeDescendants': true}, (c: FormControl) => {
+                    // An Angular custom validator
+                    if (c.value.orgIds && c.value.orgIds.length > 5) {
+                        return { tooMany: 'That\'s too many bad libraries!' };
+                    } else {
+                        return null;
+                    }
+            } )
+        });
+
+        this.badOrgForm.get('badOrgSelector').valueChanges.subscribe(bad => {
+            this.toast.danger('The worst libraries are: ' + JSON.stringify(bad.orgIds));
+        });
 
         this.gridDataSource.data = [
             {name: 'Jane', state: 'AZ'},
diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.module.ts b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.module.ts
index 58910dddbb..ec817d0d51 100644
--- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.module.ts
@@ -2,6 +2,7 @@ import {NgModule} from '@angular/core';
 import {StaffCommonModule} from '@eg/staff/common.module';
 import {SandboxRoutingModule} from './routing.module';
 import {SandboxComponent} from './sandbox.component';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
 
 @NgModule({
   declarations: [
@@ -10,6 +11,8 @@ import {SandboxComponent} from './sandbox.component';
   imports: [
     StaffCommonModule,
     SandboxRoutingModule,
+    FormsModule,
+    ReactiveFormsModule
   ],
   providers: [
   ]
diff --git a/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.html b/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.html
index 855f196e51..ab6c263249 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.html
@@ -12,36 +12,12 @@
 <eg-string #createErrString [template]="createErrStrTmpl"></eg-string>
 
 <ng-container *ngIf="orgField">
-  <div class="d-flex">
-    <div>
-      <div class="input-group">
-        <div class="input-group-prepend">
-          <span class="input-group-text">{{orgFieldLabel}}</span>
-        </div>
-        <eg-org-select 
-          [limitPerms]="viewPerms"
-          [initialOrg]="contextOrg"
-          (onChange)="orgOnChange($event)">
-        </eg-org-select>
-      </div>
-    </div>
-    <div class="pl-2">
-      <div class="form-check">
-        <input type="checkbox" (click)="grid.reload()" 
-          [disabled]="disableAncestorSelector()"
-          [(ngModel)]="includeOrgAncestors"
-          class="form-check-input" id="include-ancestors">
-        <label class="form-check-label" for="include-ancestors" i18n>+ Ancestors</label>
-      </div>
-      <div class="form-check">
-        <input type="checkbox" (click)="grid.reload()" 
-          [disabled]="disableDescendantSelector()"
-          [(ngModel)]="includeOrgDescendants" 
-          class="form-check-input" id="include-descendants">
-        <label class="form-check-label" for="include-descendants" i18n>+ Descendants</label>
-      </div>
-    </div>
-  </div>
+  <eg-org-family-select
+    [limitPerms]="viewPerms" 
+    [selectedOrgId]="contextOrg.id()"
+    [(ngModel)]="searchOrgs"
+    (ngModelChange)="grid.reload()">
+  </eg-org-family-select>
   <hr/>
 </ng-container>
 
diff --git a/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts b/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts
index a1dc1c61aa..88f9525d7d 100644
--- a/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts
+++ b/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts
@@ -12,6 +12,7 @@ import {PermService} from '@eg/core/perm.service';
 import {AuthService} from '@eg/core/auth.service';
 import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
 import {StringComponent} from '@eg/share/string/string.component';
+import {OrgFamily} from '@eg/share/org-family-select/org-family-select.component';
 
 /**
  * General purpose CRUD interface for IDL objects
@@ -83,6 +84,7 @@ export class AdminPageComponent implements OnInit {
     translatableFields: string[];
 
     contextOrg: IdlObject;
+    searchOrgs: OrgFamily;
     orgFieldLabel: string;
     viewPerms: string;
     canCreate: boolean;
@@ -124,6 +126,7 @@ export class AdminPageComponent implements OnInit {
         if (this.orgField) {
             this.orgFieldLabel = this.idlClassDef.field_map[this.orgField].label;
             this.contextOrg = this.org.get(orgId) || this.org.root();
+            this.searchOrgs = {primaryOrgId: this.contextOrg.id()};
         }
     }
 
@@ -188,11 +191,6 @@ export class AdminPageComponent implements OnInit {
         });
     }
 
-    orgOnChange(org: IdlObject) {
-        this.contextOrg = org;
-        this.grid.reload();
-    }
-
     initDataSource() {
         this.dataSource = new GridDataSource();
 
@@ -222,24 +220,7 @@ export class AdminPageComponent implements OnInit {
 
             const search: any = {};
 
-            if (this.contextOrg) {
-                // Filter rows by those linking to the context org and
-                // optionally ancestor and descendant org units.
-
-                let orgs = [this.contextOrg.id()];
-
-                if (this.includeOrgAncestors) {
-                    orgs = this.org.ancestors(this.contextOrg, true);
-                }
-
-                if (this.includeOrgDescendants) {
-                    // can result in duplicate workstation org IDs... meh
-                    orgs = orgs.concat(
-                        this.org.descendants(this.contextOrg, true));
-                }
-
-                search[this.orgField] = orgs;
-            }
+            search[this.orgField] = this.searchOrgs.orgIds || [this.contextOrg.id()];
 
             if (this.gridFilters) {
                 // Lay the URL grid filters over our search object.
@@ -253,15 +234,6 @@ export class AdminPageComponent implements OnInit {
         };
     }
 
-    disableAncestorSelector(): boolean {
-        return this.contextOrg &&
-            this.contextOrg.id() === this.org.root().id();
-    }
-
-    disableDescendantSelector(): boolean {
-        return this.contextOrg && this.contextOrg.children().length === 0;
-    }
-
     showEditDialog(idlThing: IdlObject): Promise<any> {
         this.editDialog.mode = 'update';
         this.editDialog.recId = idlThing[this.pkeyField]();

-----------------------------------------------------------------------

Summary of changes:
 .../org-family-select.component.html               |  31 ++++
 .../org-family-select.component.spec.ts            | 115 +++++++++++++++
 .../org-family-select.component.ts                 | 163 +++++++++++++++++++++
 Open-ILS/src/eg2/src/app/staff/common.module.ts    |   7 +-
 .../src/app/staff/sandbox/sandbox.component.html   |  32 ++++
 .../eg2/src/app/staff/sandbox/sandbox.component.ts |  18 +++
 .../eg2/src/app/staff/sandbox/sandbox.module.ts    |   3 +
 .../share/admin-page/admin-page.component.html     |  36 +----
 .../staff/share/admin-page/admin-page.component.ts |  36 +----
 9 files changed, 378 insertions(+), 63 deletions(-)
 create mode 100644 Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.html
 create mode 100644 Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.spec.ts
 create mode 100644 Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.ts


hooks/post-receive
-- 
Evergreen ILS



More information about the open-ils-commits mailing list