Skip to content

Commit

Permalink
feat(editors): add compositeEditorFormOrder option
Browse files Browse the repository at this point in the history
- also fix Editors.longText (textarea) number of rows, if provided in a Composite Editor moda we should use it, however never go over 3 rows.
- also a few more material for LIG project
  • Loading branch information
ghiscoding committed May 14, 2021
1 parent b4b0a17 commit 03f2d66
Show file tree
Hide file tree
Showing 11 changed files with 97 additions and 8 deletions.
5 changes: 5 additions & 0 deletions examples/webpack-demo-vanilla-bundle/src/examples/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,13 @@ export class Icons {
'.mdi.mdi-drag-vertical',
'.mdi.mdi-eye-off-outline',
'.mdi.mdi-eye-outline',
'.mdi.mdi-file-cad',
'.mdi.mdi-file-check',
'.mdi.mdi-file-check-outline',
'.mdi.mdi-file-document-outline',
'.mdi.mdi-file-excel-outline',
'.mdi.mdi-file-move',
'.mdi.mdi-file-move-outline',
'.mdi.mdi-file-music-outline',
'.mdi.mdi-file-pdf-outline',
'.mdi.mdi-file-search-outline',
Expand Down Expand Up @@ -188,6 +191,8 @@ export class Icons {
'.mdi.mdi-sync-circle',
'.mdi.mdi-table-edit',
'.mdi.mdi-table-refresh',
'.mdi.mdi-text-box-remove',
'.mdi.mdi-text-box-remove-outline',
'.mdi.mdi-text-box-search-outline',
'.mdi.mdi-toggle-switch',
'.mdi.mdi-toggle-switch-off-outline',
Expand Down
5 changes: 3 additions & 2 deletions packages/common/src/editors/longTextEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export class LongTextEditor implements Editor {
const title = this.columnEditor?.title ?? '';
const maxLength = this.columnEditor?.maxLength;
const textAreaCols = this.editorOptions?.cols ?? 40;
const textAreaRows = compositeEditorOptions ? 3 : this.editorOptions?.rows ?? 4;
const textAreaRows = this.editorOptions?.rows ?? 4;

const containerElm = compositeEditorOptions ? this.args.container : document.body;
this._wrapperElm = document.createElement('div');
Expand All @@ -131,7 +131,8 @@ export class LongTextEditor implements Editor {

this._textareaElm = document.createElement('textarea');
this._textareaElm.cols = textAreaCols;
this._textareaElm.rows = textAreaRows;
// use textarea row if defined but don't go over 3 rows with composite editor modal
this._textareaElm.rows = (compositeEditorOptions && textAreaRows > 3) ? 3 : textAreaRows;
this._textareaElm.placeholder = placeholder;
this._textareaElm.title = title;
this._wrapperElm.appendChild(this._textareaElm);
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/filters/selectFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import { CollectionService } from '../services/collection.service';
import { collectionObserver, propertyObserver } from '../services/observers';
import { getDescendantProperty, getTranslationPrefix, htmlEncode, sanitizeTextByAvailableSanitizer, unsubscribeAll } from '../services/utilities';
import { RxJsFacade, Subscription, TranslaterService } from '../services';
import { RxJsFacade, Subscription, TranslaterService } from '../services/index';
import { renderCollectionOptionsAsync } from './filterUtilities';

export class SelectFilter implements Filter {
Expand Down
6 changes: 6 additions & 0 deletions packages/common/src/interfaces/columnEditor.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ export interface ColumnEditor {
*/
complexObjectPath?: string;

/**
* When using Composite Editor Modal, the inputs will show up with the order they were entered in the column definitions array.
* You can use this option to provide a specific order to show these inputs in the form.
*/
compositeEditorFormOrder?: number;

/** A custom structure can be used instead of the default label/value pair. Commonly used with Select/Multi-Select Editor */
customStructure?: CollectionCustomStructure;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SlickGrid } from './slickGrid.interface';
import { ContainerService } from '../services';
import { ContainerService } from '../services/index';

export interface ExternalResource {
/** optionally provide the Service class name of the resource to make it easier to find even with minified code */
Expand Down
2 changes: 0 additions & 2 deletions packages/common/src/services/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,8 +285,6 @@ export function findItemInTreeStructure<T = any>(treeArray: T[], predicate: (ite
return undefined;
}



/**
* HTML encode using jQuery with a <div>
* Create a in-memory div, set it's inner text(which jQuery automatically encodes)
Expand Down
25 changes: 25 additions & 0 deletions packages/common/src/styles/material-svg-icons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,11 @@ $icon-height: $icon-width;
"M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9M12,4.5C17,4.5 21.27,7.61 23,12C21.27,16.39 17,19.5 12,19.5C7,19.5 2.73,16.39 1,12C2.73,7.61 7,4.5 12,4.5M3.18,12C4.83,15.36 8.24,17.5 12,17.5C15.76,17.5 19.17,15.36 20.82,12C19.17,8.64 15.76,6.5 12,6.5C8.24,6.5 4.83,8.64 3.18,12Z",
encodecolor($icon-color), $icon-height, $icon-width, inline-block);

@include loadsvg(
".mdi.mdi-file-cad",
"M6 2C4.9 2 4 2.9 4 4V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V8L14 2M13 3.5L18.5 9H13M9.88 9.25H11.12V10.19C11.81 10.18 12.38 10.75 12.38 11.44V13.5L12.26 13.63L13.15 15.17C13.47 14.67 13.63 14.09 13.62 13.5H14.88C14.88 14.54 14.5 15.55 13.83 16.35L15.5 19.25V20.5L14.42 19.88L12.87 17.19C12.17 17.65 11.34 17.89 10.5 17.89C9.66 17.89 8.84 17.65 8.13 17.19L6.58 19.88L5.5 20.5V19.25L8.74 13.63L8.62 13.5V11.44C8.62 10.75 9.19 10.18 9.88 10.19M10.5 11.44C9.81 11.44 9.46 12.28 9.95 12.77C10.44 13.26 11.28 12.92 11.28 12.22C11.28 11.79 10.93 11.44 10.5 11.44M9.66 14.54L8.76 16.11C9.81 16.82 11.19 16.82 12.24 16.11L11.34 14.54C10.87 15 10.13 15 9.66 14.54Z",
encodecolor($icon-color), $icon-height, $icon-width, inline-block);

@include loadsvg(
".mdi.mdi-file-check",
"M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M11.2,18.46L15.95,13.71L14.78,12.3L11.2,15.88L9.61,14.3L8.45,15.46L11.2,18.46Z",
Expand All @@ -461,6 +466,16 @@ $icon-height: $icon-width;
"M14 2H6C4.89 2 4 2.9 4 4V20C4 21.11 4.89 22 6 22H18C19.11 22 20 21.11 20 20V8L14 2M18 20H6V4H13V9H18V20M12.9 14.5L15.8 19H14L12 15.6L10 19H8.2L11.1 14.5L8.2 10H10L12 13.4L14 10H15.8L12.9 14.5Z",
encodecolor($icon-color), $icon-height, $icon-width, inline-block);

@include loadsvg(
".mdi.mdi-file-move",
"M14,17H18V14L23,18.5L18,23V20H14V17M13,9H18.5L13,3.5V9M6,2H14L20,8V12.34C19.37,12.12 18.7,12 18,12A6,6 0 0,0 12,18C12,19.54 12.58,20.94 13.53,22H6C4.89,22 4,21.1 4,20V4A2,2 0 0,1 6,2Z",
encodecolor($icon-color), $icon-height, $icon-width, inline-block);

@include loadsvg(
".mdi.mdi-file-move-outline",
"M14 2H6C4.9 2 4 2.9 4 4V20C4 20.41 4.12 20.8 4.34 21.12C4.41 21.23 4.5 21.33 4.59 21.41C4.95 21.78 5.45 22 6 22H13.53C13 21.42 12.61 20.75 12.35 20H6V4H13V9H18V12C18.7 12 19.37 12.12 20 12.34V8L14 2M18 23L23 18.5L20 15.8L18 14V17H14V20H18V23Z",
encodecolor($icon-color), $icon-height, $icon-width, inline-block);

@include loadsvg(
".mdi.mdi-file-music-outline",
"M14,2L20,8V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2H14M18,20V9H13V4H6V20H18M13,10V12H11V17A2,2 0 0,1 9,19A2,2 0 0,1 7,17A2,2 0 0,1 9,15C9.4,15 9.7,15.1 10,15.3V10H13Z",
Expand Down Expand Up @@ -816,6 +831,16 @@ $icon-height: $icon-width;
"M18 14.5C19.11 14.5 20.11 14.95 20.83 15.67L22 14.5V18.5H18L19.77 16.73C19.32 16.28 18.69 16 18 16C16.62 16 15.5 17.12 15.5 18.5C15.5 19.88 16.62 21 18 21C18.82 21 19.55 20.61 20 20H21.71C21.12 21.47 19.68 22.5 18 22.5C15.79 22.5 14 20.71 14 18.5C14 16.29 15.79 14.5 18 14.5M4 3H18C19.11 3 20 3.9 20 5V12.17C19.5 12.06 19 12 18.5 12C17.23 12 16.04 12.37 15.04 13H12V17H12.18C12.06 17.5 12 18 12 18.5L12 19H4C2.9 19 2 18.11 2 17V5C2 3.9 2.9 3 4 3M4 7V11H10V7H4M12 7V11H18V7H12M4 13V17H10V13H4Z",
encodecolor($icon-color), $icon-height, $icon-width, inline-block);

@include loadsvg(
".mdi.mdi-text-box-remove",
"M14.46,15.88L15.88,14.46L18,16.59L20.12,14.46L21.54,15.88L19.41,18L21.54,20.12L20.12,21.54L18,19.41L15.88,21.54L14.46,20.12L16.59,18L14.46,15.88M12,17V15H7V17H12M17,11H7V13H14.69C13.07,14.07 12,15.91 12,18C12,19.09 12.29,20.12 12.8,21H5C3.89,21 3,20.1 3,19V5C3,3.89 3.89,3 5,3H19A2,2 0 0,1 21,5V12.8C20.12,12.29 19.09,12 18,12L17,12.08V11M17,9V7H7V9H17Z",
encodecolor($icon-color), $icon-height, $icon-width, inline-block);

@include loadsvg(
".mdi.mdi-text-box-remove-outline",
"M14.46,15.88L15.88,14.46L18,16.59L20.12,14.46L21.54,15.88L19.41,18L21.54,20.12L20.12,21.54L18,19.41L15.88,21.54L14.46,20.12L16.59,18L14.46,15.88M5,3H19C20.11,3 21,3.89 21,5V12.8C20.39,12.45 19.72,12.2 19,12.08V5H5V19H12.08C12.2,19.72 12.45,20.39 12.8,21H5C3.89,21 3,20.11 3,19V5C3,3.89 3.89,3 5,3M7,7H17V9H7V7M7,11H17V12.08C16.15,12.22 15.37,12.54 14.68,13H7V11M7,15H12V17H7V15Z",
encodecolor($icon-color), $icon-height, $icon-width, inline-block);

@include loadsvg(
".mdi.mdi-text-box-search-outline",
"M15.5,12C18,12 20,14 20,16.5C20,17.38 19.75,18.21 19.31,18.9L22.39,22L21,23.39L17.88,20.32C17.19,20.75 16.37,21 15.5,21C13,21 11,19 11,16.5C11,14 13,12 15.5,12M15.5,14A2.5,2.5 0 0,0 13,16.5A2.5,2.5 0 0,0 15.5,19A2.5,2.5 0 0,0 18,16.5A2.5,2.5 0 0,0 15.5,14M5,3H19C20.11,3 21,3.89 21,5V13.03C20.5,12.23 19.81,11.54 19,11V5H5V19H9.5C9.81,19.75 10.26,20.42 10.81,21H5C3.89,21 3,20.11 3,19V5C3,3.89 3.89,3 5,3M7,7H17V9H7V7M7,11H12.03C11.23,11.5 10.54,12.19 10,13H7V11M7,15H9.17C9.06,15.5 9,16 9,16.5V17H7V15Z",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,49 @@ describe('CompositeEditorService', () => {
expect(productNameDetailContainerElm).toBeTruthy();
});

it('should make sure Slick-Composite-Editor is being created and expect form inputs to be in specific order when user provides column def "compositeEditorFormOrder"', () => {
const mockProduct = { id: 222, address: { zip: 123456 }, productName: 'Product ABC', price: 12.55 };
const sortedColumnsMock = [
{ id: 'age', field: 'age', width: 100, name: 'Age', editor: { model: Editors.float, compositeEditorFormOrder: 2, } },
{ id: 'middleName', field: 'middleName', width: 100, name: 'Middle Name', editor: { model: Editors.text } },
{ id: 'lastName', field: 'lastName', width: 100, name: 'Last Name', editor: { model: Editors.text, compositeEditorFormOrder: 1, } },
{ id: 'firstName', field: 'firstName', width: 100, name: 'First Name', editor: { model: Editors.text, compositeEditorFormOrder: 0, } },
] as Column[];
sortedColumnsMock.forEach(col => col.internalColumnEditor = col.editor); // do the editor swap that the lib does internally
jest.spyOn(gridStub, 'getColumns').mockReturnValue(sortedColumnsMock);
jest.spyOn(gridStub, 'getDataItem').mockReturnValue(mockProduct);

component = new SlickCompositeEditorComponent();
component.init(gridStub, container);
component.openDetails({ headerTitle: 'Details' });

const compositeContainerElm = document.querySelector('div.slick-editor-modal.slickgrid_123456') as HTMLSelectElement;
const compositeHeaderElm = compositeContainerElm.querySelector('.slick-editor-modal-header') as HTMLSelectElement;
const compositeTitleElm = compositeHeaderElm.querySelector('.slick-editor-modal-title') as HTMLSelectElement;
const compositeBodyElm = compositeContainerElm.querySelector('.slick-editor-modal-body') as HTMLSelectElement;
const itemDetailsContainerElm = compositeBodyElm.querySelectorAll<HTMLSelectElement>('.item-details-container');

expect(component).toBeTruthy();
expect(component.constructor).toBeDefined();
expect(compositeContainerElm).toBeTruthy();
expect(compositeHeaderElm).toBeTruthy();
expect(compositeTitleElm).toBeTruthy();
expect(compositeTitleElm.textContent).toBe('Details');
expect(compositeBodyElm).toBeTruthy();

// it shouldn't have the order it was added in the column definitions array
expect(itemDetailsContainerElm[0].classList.contains('editor-age')).toBeFalse();
expect(itemDetailsContainerElm[1].classList.contains('editor-middleName')).toBeFalse();
expect(itemDetailsContainerElm[2].classList.contains('editor-lastName')).toBeFalse();
expect(itemDetailsContainerElm[3].classList.contains('editor-firstName')).toBeFalse();

// but it should have the order it was defined by `compositeEditorFormOrder` sort order
expect(itemDetailsContainerElm[0].classList.contains('editor-firstName')).toBeTrue();
expect(itemDetailsContainerElm[1].classList.contains('editor-lastName')).toBeTrue();
expect(itemDetailsContainerElm[2].classList.contains('editor-age')).toBeTrue();
expect(itemDetailsContainerElm[3].classList.contains('editor-middleName')).toBeTrue();
});

it('should make sure Slick-Composite-Editor is being created and rendered with 2 columns layout when having more than 8 but less than 15 column definitions', () => {
const copyColumnsMock: Column[] = createNewColumDefinitions(8);
jest.spyOn(gridStub, 'getColumns').mockReturnValue(copyColumnsMock);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
GridService,
GridStateService,
Locale,
numericSortComparer,
OnErrorOption,
OnCompositeEditorChangeEventArgs,
PlainFunc,
Expand All @@ -33,6 +34,7 @@ import {
SlickEventHandler,
SlickGrid,
SlickNamespace,
SortDirectionNumber,
TranslaterService,
} from '@slickgrid-universal/common';

Expand Down Expand Up @@ -343,6 +345,15 @@ export class SlickCompositeEditorComponent implements ExternalResource {
modalColumns = this._columnDefinitions.filter(col => col.editor);
}

// user could optionally show the form inputs in a specific order instead of using default column definitions order
if (modalColumns.some(col => col.internalColumnEditor?.compositeEditorFormOrder !== undefined)) {
modalColumns.sort((col1: Column, col2: Column) => {
const val1 = col1?.internalColumnEditor?.compositeEditorFormOrder ?? Infinity;
const val2 = col2?.internalColumnEditor?.compositeEditorFormOrder ?? Infinity;
return numericSortComparer(val1, val2, SortDirectionNumber.asc);
});
}

// open the editor modal and we can also provide a header title with optional parsing pulled from the dataContext, via template {{ }}
// for example {{title}} => display the item title, or even complex object works {{product.name}} => display item product name
const parsedHeaderTitle = headerTitle.replace(/\{\{(.*?)\}\}/g, (_match, group) => getDescendantProperty(dataContext, group));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { BackendServiceApi, Observable } from '@slickgrid-universal/common';
import { GraphqlResult } from './graphqlResult.interface';
import { GraphqlPaginatedResult } from './graphqlPaginatedResult.interface';
import { GraphqlServiceOption } from './graphqlServiceOption.interface';
import { GraphqlService } from '../services';
import { GraphqlService } from '../services/index';

export interface GraphqlServiceApi extends BackendServiceApi {
/** Backend Service Options */
Expand Down
2 changes: 1 addition & 1 deletion packages/odata/src/interfaces/odataServiceApi.interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BackendServiceApi } from '@slickgrid-universal/common';
import { OdataOption } from './odataOption.interface';
import { GridOdataService } from '../services';
import { GridOdataService } from '../services/index';

export interface OdataServiceApi extends BackendServiceApi {
/** Backend Service Options */
Expand Down

0 comments on commit 03f2d66

Please sign in to comment.