LP 1857351: admin-page grid column order respects specified fieldOrder
authorJane Sandberg <sandbej@linnbenton.edu>
Fri, 26 Jun 2020 07:33:08 +0000 (07:33 +0000)
committerJane Sandberg <sandbej@linnbenton.edu>
Thu, 28 Jan 2021 18:34:21 +0000 (10:34 -0800)
Grid columns that aren't auto-generated and grids without a
specified autoGeneratedColumnOrder are not affected.

Signed-off-by: Jane Sandberg <sandbej@linnbenton.edu>
Signed-off-by: Ruth Frasur <rfrasur@library.in.gov>
Signed-off-by: Michele Morgan <mmorgan@noblenet.org>
Open-ILS/src/eg2/src/app/core/idl.service.ts
Open-ILS/src/eg2/src/app/core/idl.spec.ts
Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts
Open-ILS/src/eg2/src/app/share/grid/grid.component.ts
Open-ILS/src/eg2/src/app/share/grid/grid.ts
Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.html

index 040bbba..4f00c53 100644 (file)
@@ -227,5 +227,35 @@ export class IdlService {
         const pkeyField = this.classes[idlClass].pkey || 'id';
         return obj1[pkeyField]() === obj2[pkeyField]();
     }
+
+    // Sort an array of fields from the IDL (like you might get from calling
+    // this.idlClasses[classname][fields])
+
+    sortIdlFields(fields: any[], desiredOrder: string[]): any[] {
+        let newList = [];
+
+        desiredOrder.forEach(name => {
+            const match = fields.filter(field => field.name === name)[0];
+            if (match) { newList.push(match); }
+        });
+
+        // Sort remaining fields by label
+        const remainder = fields.filter(f => !desiredOrder.includes(f.name));
+        remainder.sort((a, b) => {
+            if (a.label && b.label) {
+                return (a.label < b.label) ? -1 : 1;
+            } else if (a.label) {
+                return -1;
+            } else if (b.label) {
+                return 1;
+            }
+
+            // If no order specified and no labels to sort by,
+            // default to sorting by field name
+            return (a.label < b.label) ? -1 : 1;
+        });
+        newList = newList.concat(remainder);
+        return newList;
+    }
 }
 
index 49439fd..a97108d 100644 (file)
@@ -51,6 +51,54 @@ describe('IdlService', () => {
         expect(service.pkeyMatches(org, user)).toBe(false);
     });
 
+    it('should sort an array of IDL fields according to an array of field names', () => {
+        const fieldNames = ['name', 'owner', 'active', 'id'];
+        const idlFields = [
+            {'name': 'id', 'label': 'Object ID', 'dataType': 'id'},
+            {'name': 'name', 'label': 'The name of this object', 'datatype': 'text'},
+            {'name': 'active', 'datatype': 'bool'},
+            {'name': 'owner', 'type': 'link', 'key': 'id', 'class': 'aou', 'reltype': 'has_a', 'datatype': 'org_unit'}
+        ];
+        const expectedOrder = [
+            {'name': 'name', 'label': 'The name of this object', 'datatype': 'text'},
+            {'name': 'owner', 'type': 'link', 'key': 'id', 'class': 'aou', 'reltype': 'has_a', 'datatype': 'org_unit'},
+            {'name': 'active', 'datatype': 'bool'},
+            {'name': 'id', 'label': 'Object ID', 'dataType': 'id'},
+        ];
+        expect(service.sortIdlFields(idlFields, fieldNames)).toEqual(expectedOrder);
+    });
+
+    it('should sort IDL fields by label when it runs out of specified field names', () => {
+        const fieldNames = ['owner'];
+        const idlFields = [
+            {'name': 'id', 'label': 'Object ID', 'dataType': 'id'},
+            {'name': 'name', 'label': 'The name of this object', 'datatype': 'text'},
+            {'name': 'owner', 'type': 'link', 'key': 'id', 'class': 'aou', 'reltype': 'has_a', 'datatype': 'org_unit'}
+        ];
+        const expectedOrder = [
+            {'name': 'owner', 'type': 'link', 'key': 'id', 'class': 'aou', 'reltype': 'has_a', 'datatype': 'org_unit'},
+            {'name': 'id', 'label': 'Object ID', 'dataType': 'id'},
+            {'name': 'name', 'label': 'The name of this object', 'datatype': 'text'},
+        ];
+        expect(service.sortIdlFields(idlFields, fieldNames)).toEqual(expectedOrder);
+    });
+
+    it('should sort IDL fields by name when it runs out of other ways to sort', () => {
+        const fieldNames = ['owner'];
+        const idlFields = [
+            {'name': 'id', 'dataType': 'id'},
+            {'name': 'name', 'label': 'The name of this object', 'datatype': 'text'},
+            {'name': 'active', 'datatype': 'bool'},
+            {'name': 'owner', 'type': 'link', 'key': 'id', 'class': 'aou', 'reltype': 'has_a', 'datatype': 'org_unit'}
+        ];
+        const expectedOrder = [
+            {'name': 'owner', 'type': 'link', 'key': 'id', 'class': 'aou', 'reltype': 'has_a', 'datatype': 'org_unit'},
+            {'name': 'name', 'label': 'The name of this object', 'datatype': 'text'},
+            {'name': 'active', 'datatype': 'bool'},
+            {'name': 'id', 'dataType': 'id'},
+        ];
+        expect(service.sortIdlFields(idlFields, fieldNames)).toEqual(expectedOrder);
+    });
 
 });
 
index 4b6ee5e..4ac2e2e 100644 (file)
@@ -447,26 +447,8 @@ export class FmRecordEditorComponent
             fields.map(field => this.constructOneField(field))
 
         ).then(() => {
-
-            if (!this.fieldOrder) {
-                this.fields = fields.sort((a, b) => a.label < b.label ? -1 : 1);
-                return;
-            }
-
-            let newList = [];
-            const ordered = this.fieldOrder.split(/,/);
-
-            ordered.forEach(name => {
-                const f1 = fields.filter(f2 => f2.name === name)[0];
-                if (f1) { newList.push(f1); }
-            });
-
-            // Sort remaining fields by label
-            const remainder = fields.filter(f => !ordered.includes(f.name));
-            remainder.sort((a, b) => a.label < b.label ? -1 : 1);
-            newList = newList.concat(remainder);
-
-            this.fields = newList;
+            const order = this.fieldOrder ? this.fieldOrder.split(/,/) : [];
+            this.fields = this.idl.sortIdlFields(fields, order);
         });
     }
 
index 23996ee..4c0885a 100644 (file)
@@ -30,6 +30,10 @@ export class GridComponent implements OnInit, AfterViewInit, OnDestroy {
     // IDL class for auto-generation of columns
     @Input() idlClass: string;
 
+    // Comma-separated list of column names, to set the order of columns
+    // from auto-generated columns only
+    @Input() autoGeneratedColumnOrder: string;
+
     // True if any columns are sortable
     @Input() sortable: boolean;
 
@@ -146,6 +150,7 @@ export class GridComponent implements OnInit, AfterViewInit, OnDestroy {
         this.context.idlClass = this.idlClass;
         this.context.dataSource = this.dataSource;
         this.context.persistKey = this.persistKey;
+        this.context.autoGeneratedColumnOrder = this.autoGeneratedColumnOrder;
         this.context.isSortable = this.sortable === true;
         this.context.isFilterable = this.filterable === true;
         this.context.stickyGridHeader = this.stickyHeader === true;
index f4e4396..c45acb3 100644 (file)
@@ -493,6 +493,7 @@ export class GridContext {
     disableSelect: boolean;
     dataSource: GridDataSource;
     columnSet: GridColumnSet;
+    autoGeneratedColumnOrder: string;
     rowSelector: GridRowSelector;
     toolbarButtons: GridToolbarButton[];
     toolbarCheckboxes: GridToolbarCheckbox[];
@@ -1075,11 +1076,18 @@ export class GridContext {
         if (!this.columnSet.idlClass) { return; }
 
         const pkeyField = this.idl.classes[this.columnSet.idlClass].pkey;
+        const specifiedColumnOrder = this.autoGeneratedColumnOrder ?
+            this.autoGeneratedColumnOrder.split(/,/) : [];
 
         // generate columns for all non-virtual fields on the IDL class
-        this.idl.classes[this.columnSet.idlClass].fields
-        .filter(field => !field.virtual)
-        .forEach(field => {
+        const fields = this.idl.classes[this.columnSet.idlClass].fields
+            .filter(field => !field.virtual);
+
+        const sortedFields = this.autoGeneratedColumnOrder ?
+            this.idl.sortIdlFields(fields, this.autoGeneratedColumnOrder.split(/,/)) :
+            fields;
+
+        sortedFields.forEach(field => {
             if (!this.ignoredFields.filter(ignored => ignored === field.name).length) {
                 const col = new GridColumn();
                 col.name = field.name;
index a7d7123..f6e6397 100644 (file)
@@ -53,8 +53,8 @@
 </ng-template>
 
 <eg-grid #grid idlClass="{{idlClass}}" [dataSource]="dataSource" hideFields="{{hideGridFields}}"
-    [sortable]="true" persistKey="{{persistKey}}" [filterable]="true">
-    [stickyHeader]="true">
+    [sortable]="true" persistKey="{{persistKey}}" autoGeneratedColumnOrder="{{fieldOrder}}"
+    [filterable]="true" [stickyHeader]="true">
   <eg-grid-toolbar-button [disabled]="!canCreate" 
     label="New {{idlClassDef.label}}" i18n-label (onClick)="createNew()">
   </eg-grid-toolbar-button>