Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(editors): add changeEditorOption to all Editors which supports it #142

Merged
merged 10 commits into from
Oct 27, 2020
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
command: yarn run bundle
- run:
name: Run Jest tests with JUnit as reporter
command: ./node_modules/.bin/jest --config test/jest.config.js --ci --runInBand --collectCoverage=true --reporters=default --reporters=jest-junit
command: ./node_modules/.bin/jest --config test/jest.config.js --ci --runInBand --collectCoverage=true --reporters=jest-junit
environment:
JEST_JUNIT_OUTPUT: "reports/junit/js-test-results.xml"
- run:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Note however that this project also has a Vanilla Implementation (not associated
and it is also used to test with the UI portion. The Vanilla bundle is also used in our SalesForce (with Lightning Web Component) hence the creation of this monorepo.

### Fully Tested with [Jest](https://jestjs.io/) (Unit Tests) - [Cypress](https://www.cypress.io/) (E2E Tests)
Slickgrid-Universal has **100%** Unit Test Coverage, we are talking about 10,000 lines of code (+2,800 unit tests) that are now fully tested with [Jest](https://jestjs.io/). There are also ~200 Cypress E2E tests to cover all [Examples](https://ghiscoding.github.io/slickgrid-universal/) and most UI functionalities.
Slickgrid-Universal has **100%** Unit Test Coverage, we are talking about +10,000 lines of code (+2,800 unit tests) that are now fully tested with [Jest](https://jestjs.io/). There are also +200 Cypress E2E tests to cover all [Examples](https://ghiscoding.github.io/slickgrid-universal/) and most UI functionalities.

### Available Public Packages

Expand Down
7 changes: 3 additions & 4 deletions examples/webpack-demo-vanilla-bundle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,18 @@
"devDependencies": {
"@types/jquery": "^3.5.3",
"@types/moment": "^2.13.0",
"@types/node": "^14.11.8",
"@types/webpack": "^4.41.22",
"@types/node": "^14.14.3",
"@types/webpack": "^4.41.23",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^6.2.1",
"css-loader": "^4.3.0",
"file-loader": "^6.1.1",
"fork-ts-checker-webpack-plugin": "^5.2.0",
"html-loader": "^1.3.2",
"html-webpack-plugin": "^4.5.0",
"json-loader": "^0.5.7",
"mini-css-extract-plugin": "^0.12.0",
"node-sass": "4.14.1",
"sass-loader": "^10.0.3",
"sass-loader": "^10.0.4",
"style-loader": "^1.3.0",
"ts-loader": "^8.0.5",
"ts-node": "^9.0.0",
Expand Down
18 changes: 16 additions & 2 deletions examples/webpack-demo-vanilla-bundle/src/examples/example12.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,19 @@ export class Example12 {
formatter: Formatters.dollar,
},
{
id: 'percentComplete', name: '% Complete', field: 'percentComplete', type: FieldType.number,
id: 'percentComplete', name: '% Complete', field: 'percentComplete',
type: FieldType.number,
sortable: true, filterable: true, columnGroup: 'Analysis',
filter: { model: Filters.compoundSlider, operator: '>=' },
// formatter: Formatters.collectionEditor,
editor: {
model: Editors.slider,
// model: Editors.singleSelect,
// enableRenderHtml: true,
// collection: Array.from(Array(101).keys()).map(k => ({ value: k, label: k, symbol: ' <i class="mdi mdi-check-circle color-primary"></i>' })),
// collection: Array.from(Array(101).keys()).map(k => ({ value: k, label: k, symbol: ' <i class="mdi mdi-calendar-check color-primary"></i>' })),
// collectionOptions: {
// addCustomFirstEntry: { value: '', label: '--none--' }
// },
// customStructure: {
// value: 'value',
// label: 'label',
Expand Down Expand Up @@ -410,6 +414,7 @@ export class Example12 {

if (!(i % 8)) {
delete tmpArray[i].finish; // also test with undefined properties
delete tmpArray[i].percentComplete; // also test with undefined properties
}
}
return tmpArray;
Expand Down Expand Up @@ -487,7 +492,16 @@ export class Example12 {
this.sgb.slickCompositeEditor.changeFormInputValue('completed', true);
this.sgb.slickCompositeEditor.changeFormInputValue('finish', new Date());
// this.sgb.slickCompositeEditor.changeFormInputValue('product', { id: 0, itemName: 'Sleek Metal Computer' });

}

// you can also change any editor options (not all Editors supports this functionality, so far only these Editors AutoComplete, Date MultipleSelect & SingleSelect)
/*
if (columnDef.id === 'completed' && formValues.completed) {
this.sgb.slickCompositeEditor.changeFormEditorOption('percentComplete', 'filter', true);
this.sgb.slickCompositeEditor.changeFormEditorOption('product', 'minLength', 3);
}
*/
}

handleOnSelectedRowsChanged(event) {
Expand Down
24 changes: 12 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"new-version": "lerna version --conventional-commits",
"publish": "lerna publish",
"serve:demo": "http-server ./docs -p 8888 -a localhost",
"test": "npx jest --runInBand --coverage --config ./test/jest.config.js",
"test": "npx jest --runInBand --coverage=true --config ./test/jest.config.js",
"test:watch": "npx jest --watch --config ./test/jest.config.js"
},
"workspaces": {
Expand All @@ -42,27 +42,27 @@
]
},
"devDependencies": {
"@types/jest": "^26.0.14",
"@types/node": "^14.11.8",
"@typescript-eslint/eslint-plugin": "^4.4.1",
"@typescript-eslint/parser": "^4.4.1",
"cypress": "^5.3.0",
"eslint": "^7.11.0",
"@types/jest": "^26.0.15",
"@types/node": "^14.14.3",
"@typescript-eslint/eslint-plugin": "^4.6.0",
"@typescript-eslint/parser": "^4.6.0",
"cypress": "^5.5.0",
"eslint": "^7.12.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-prefer-arrow": "^1.2.2",
"http-server": "^0.12.3",
"jest": "^26.5.3",
"jest-cli": "^26.5.3",
"jest-environment-jsdom": "^26.5.2",
"jest": "^26.6.1",
"jest-cli": "^26.6.1",
"jest-environment-jsdom": "^26.6.1",
"jest-extended": "^0.11.5",
"jest-junit": "^12.0.0",
"jsdom": "^16.4.0",
"jsdom-global": "^3.0.2",
"lerna": "^3.22.1",
"mocha": "^8.1.3",
"mocha": "^8.2.0",
"mochawesome": "^6.1.1",
"npm-run-all": "^4.1.5",
"ts-jest": "^26.4.1",
"ts-jest": "^26.4.3",
"typescript": "^4.0.3"
},
"engines": {
Expand Down
8 changes: 4 additions & 4 deletions packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@
"not dead"
],
"dependencies": {
"dompurify": "^2.1.1",
"dompurify": "^2.2.0",
"flatpickr": "^4.6.6",
"jquery": "^3.5.1",
"jquery-ui-dist": "^1.12.1",
"lodash.isequal": "^4.5.0",
"moment-mini": "^2.24.0",
"multiple-select-modified": "^1.3.4",
"multiple-select-modified": "^1.3.5",
"slickgrid": "^2.4.30"
},
"devDependencies": {
Expand All @@ -82,9 +82,9 @@
"cross-env": "^7.0.2",
"mini-css-extract-plugin": "^0.12.0",
"node-sass": "4.14.1",
"nodemon": "^2.0.5",
"nodemon": "^2.0.6",
"npm-run-all": "^4.1.5",
"postcss": "^8.1.1",
"postcss": "^8.1.4",
"postcss-cli": "^8.1.0",
"rimraf": "^3.0.2"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,14 @@ describe('AutoCompleteEditor', () => {
expect(spy).toHaveBeenCalled();
});

it('should call the "changeEditorOption" method and expect new option to be merged with the previous Editor options and also expect to call AutoComplete "option" setter method', () => {
editor = new AutoCompleteEditor(editorArguments);
const autoCompleteSpy = jest.spyOn(editor.editorDomElement, 'autocomplete');
editor.changeEditorOption('delay', 500);

expect(autoCompleteSpy).toHaveBeenCalledWith('option', 'delay', 500);
});

describe('applyValue method', () => {
it('should apply the value to the gender property when it passes validation', () => {
mockColumn.internalColumnEditor.validator = null;
Expand Down
12 changes: 11 additions & 1 deletion packages/common/src/editors/__tests__/dateEditor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ describe('DateEditor', () => {
expect(editorCount).toBe(1);
});

it('should initialize the editor and focus on the element after a small delay', (done) => {
it('should initialize the editor and expect to focus on the element after a small delay', (done) => {
const focusSpy = jest.spyOn(editor, 'focus');
const showSpy = jest.spyOn(editor, 'focus');
editor = new DateEditor(editorArguments);
Expand Down Expand Up @@ -194,6 +194,16 @@ describe('DateEditor', () => {
expect(spy).toHaveBeenCalled();
});

it('should call the "changeEditorOption" method and expect new option to be merged with the previous Editor options and also expect to call Flatpickr "set" method', () => {
editor = new DateEditor(editorArguments);
const spy = jest.spyOn(editor.flatInstance, 'set');
const calendarElm = document.body.querySelector<HTMLDivElement>('.flatpickr-calendar');
editor.changeEditorOption('minDate', 'today');

expect(calendarElm).toBeTruthy();
expect(spy).toHaveBeenCalledWith('minDate', 'today');
});

describe('isValueChanged method', () => {
it('should return True when date is changed in the picker', () => {
// change to allow input value only for testing purposes & use the regular flatpickr input to test that one too
Expand Down
8 changes: 8 additions & 0 deletions packages/common/src/editors/__tests__/selectEditor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,14 @@ describe('SelectEditor', () => {
expect(editorListElm.length).toBe(3);
expect(editor.isValueChanged()).toBe(true);
});

it('should call the "changeEditorOption" method and expect new option to be merged with the previous Editor options and also expect to call MultipleSelect "refreshOptions" setter method', () => {
editor = new SelectEditor(editorArguments, true);
const multipleSelectSpy = jest.spyOn(editor.editorDomElement, 'multipleSelect');
editor.changeEditorOption('filter', true);

expect(multipleSelectSpy).toHaveBeenCalledWith('refreshOptions', { ...editor.editorElmOptions, filter: true });
});
});

describe('applyValue method', () => {
Expand Down
15 changes: 15 additions & 0 deletions packages/common/src/editors/autoCompleteEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,21 @@ export class AutoCompleteEditor implements Editor {
this._$editorElm.off('keydown.nav').remove();
}

/**
* Dynamically change an Editor option, this is especially useful with Composite Editor
* since this is the only way to change option after the Editor is created (for example dynamically change "minDate" or another Editor)
* @param {string} optionName - MultipleSelect option name
* @param {newValue} newValue - MultipleSelect new option value
*/
changeEditorOption(optionName: keyof AutocompleteOption, newValue: any) {
if (!this.columnEditor.editorOptions) {
this.columnEditor.editorOptions = {};
}
this.columnEditor.editorOptions[optionName] = newValue;
this._autoCompleteOptions = { ...this._autoCompleteOptions, [optionName]: newValue };
this._$editorElm.autocomplete('option', optionName, newValue);
}

disable(isDisabled = true) {
const prevIsDisabled = this.disabled;
this.disabled = isDisabled;
Expand Down
15 changes: 15 additions & 0 deletions packages/common/src/editors/dateEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,21 @@ export class DateEditor implements Editor {
}
}

/**
* Dynamically change an Editor option, this is especially useful with Composite Editor
* since this is the only way to change option after the Editor is created (for example dynamically change "minDate" or another Editor)
* @param {string} optionName - Flatpickr option name
* @param {newValue} newValue - Flatpickr new option value
*/
changeEditorOption(optionName: keyof FlatpickrOption, newValue: any) {
if (!this.columnEditor.editorOptions) {
this.columnEditor.editorOptions = {};
}
this.columnEditor.editorOptions[optionName] = newValue;
this._pickerMergedOptions = { ...this._pickerMergedOptions, [optionName]: newValue };
this.flatInstance.set(optionName, newValue);
}

focus() {
this._$input.focus();
if (this._$inputWithData && typeof this._$inputWithData.focus === 'function') {
Expand Down
49 changes: 33 additions & 16 deletions packages/common/src/editors/selectEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ declare const Slick: SlickNamespace;
* Slickgrid editor class for multiple/single select lists
*/
export class SelectEditor implements Editor {
/** Locales */
protected _locales: Locale;

// flag to signal that the editor is destroying itself, helps prevent
// commit changes from being called twice and erroring
protected _destroying = false;

/** Collection Service */
protected _collectionService: CollectionService;

/** The translate library */
protected _translaterService: TranslaterService;

/** The JQuery DOM element */
$editorElm: any;

Expand Down Expand Up @@ -66,19 +79,6 @@ export class SelectEditor implements Editor {
/** Do we translate the label? */
enableTranslateLabel: boolean;

/** Locales */
protected _locales: Locale;

// flag to signal that the editor is destroying itself, helps prevent
// commit changes from being called twice and erroring
protected _destroying = false;

/** Collection Service */
protected _collectionService: CollectionService;

/** The translate library */
protected _translaterService: TranslaterService;

/** SlickGrid Grid object */
grid: SlickGrid;

Expand Down Expand Up @@ -350,7 +350,7 @@ export class SelectEditor implements Editor {

if (fieldName !== undefined) {
// when the provided user defined the column field type as a possible number then try parsing the state value as that
if (fieldType === FieldType.number || fieldType === FieldType.integer || fieldType === FieldType.boolean) {
if ((fieldType === FieldType.number || fieldType === FieldType.integer || fieldType === FieldType.boolean) && !isNaN(parseFloat(state))) {
newValue = parseFloat(state);
}

Expand All @@ -361,7 +361,7 @@ export class SelectEditor implements Editor {
}

// is the field a complex object, "user.address.streetNumber"
const isComplexObject = fieldName !== undefined && fieldName?.indexOf('.') > 0;
const isComplexObject = fieldName?.indexOf('.') > 0;

// validate the value before applying it (if not valid we'll set an empty string)
const validation = this.validate(null, newValue);
Expand All @@ -373,7 +373,7 @@ export class SelectEditor implements Editor {
// else we use the path provided in the Field Column Definition
const objectPath = this.columnEditor && this.columnEditor.complexObjectPath || fieldName || '';
setDeepValue(item, objectPath, newValue);
} else if (fieldName !== undefined) {
} else {
item[fieldName] = newValue;
}
}
Expand Down Expand Up @@ -452,6 +452,23 @@ export class SelectEditor implements Editor {
return (this.isMultipleSelect) ? this.currentValues : this.currentValue;
}

/**
* Dynamically change an Editor option, this is especially useful with Composite Editor
* since this is the only way to change option after the Editor is created (for example dynamically change "minDate" or another Editor)
* @param {string} optionName - MultipleSelect option name
* @param {newValue} newValue - MultipleSelect new option value
*/
changeEditorOption(optionName: keyof MultipleSelectOption, newValue: any) {
if (this.columnEditor) {
if (!this.columnEditor.editorOptions) {
this.columnEditor.editorOptions = {};
}
this.columnEditor.editorOptions[optionName] = newValue;
this.editorElmOptions = { ...this.editorElmOptions, [optionName]: newValue };
this.$editorElm.multipleSelect('refreshOptions', this.editorElmOptions);
}
}

disable(isDisabled = true) {
const prevIsDisabled = this.disabled;
this.disabled = isDisabled;
Expand Down
10 changes: 10 additions & 0 deletions packages/common/src/interfaces/editor.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ export interface Editor {
/** Initialize the Editor */
init: (args?: EditorArguments) => void;

/**
* Dynamically change an Editor option, this is especially useful when using a Composite Editor
* since this is the only way to change/update an option of the Editor after its creation
* (not all Editors support this feature, current only available with AutoComplete, Date, MultipleSelect & SingleSelect Editors).
* For example, a use case could be to dynamically change the "minDate" of another Date Editor in the Composite Editor form.
* @param {string} optionName - option name
* @param {newValue} newValue - new option value
*/
changeEditorOption?: (optionName: string, newValue: any) => void;

/**
* Disable the Editor input
* @param {boolean} isDisabled - optionally specify if the editor is disabled or not.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,12 @@ export interface MultipleSelectOption {
/** Reloads the Multiple Select. If you’re dynamically adding/removing option tags on the original select via AJAX or DOM manipulation methods, call refresh to reflect the changes. */
refresh?: () => void;

/** Gets the current Multiple-Select options */
getOptions?: () => MultipleSelectOption;

/** Set new multiple-select option(s) and refresh the element */
refreshOptions?: (newOptions: MultipleSelectOption) => void;

/** Gets the selected values. */
getSelects?: () => string | string[];

Expand Down
6 changes: 3 additions & 3 deletions packages/common/src/interfaces/pagingInfo.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ export interface PagingInfo {
pageNum: number;

/** Total count of rows in dataset */
totalRows?: number;
totalRows: number;

/** Total pages count that pagination has */
totalPages?: number;
totalPages: number;

/** DataView object */
dataView?: SlickDataView;
dataView: SlickDataView;
}
Loading