Skip to content

Commit

Permalink
feat(resizer): add single Column Resize by Content dblClick & headerM…
Browse files Browse the repository at this point in the history
…enu (#579)

* feat(resizer): add single Column Resize by Content dblClick & headerMenu
- takes the previous feature of resize (all) columns by content and make it a single column resize by content. It can be triggered by a double-click on the resize column hover over OR via a new header menu command

* refactor: bump slickgrid-universal and fix some code to make it all work
  • Loading branch information
ghiscoding committed May 22, 2021
1 parent 424aea4 commit 631465a
Show file tree
Hide file tree
Showing 24 changed files with 609 additions and 261 deletions.
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@
"build:demo": "webpack --progress -p --env.production --env.extractCss"
},
"dependencies": {
"@slickgrid-universal/common": "^0.14.0",
"@slickgrid-universal/empty-warning-component": "^0.14.0",
"@slickgrid-universal/common": "^0.14.1",
"@slickgrid-universal/empty-warning-component": "^0.14.1",
"aurelia-event-aggregator": "^1.0.3",
"aurelia-framework": "^1.3.1",
"aurelia-i18n": "^3.1.4",
Expand All @@ -70,12 +70,12 @@
"i18next": ">= 14.1.1"
},
"devDependencies": {
"@slickgrid-universal/composite-editor-component": "^0.14.0",
"@slickgrid-universal/excel-export": "^0.14.0",
"@slickgrid-universal/graphql": "^0.14.0",
"@slickgrid-universal/odata": "^0.14.0",
"@slickgrid-universal/rxjs-observable": "^0.14.0",
"@slickgrid-universal/text-export": "^0.14.0",
"@slickgrid-universal/composite-editor-component": "^0.14.1",
"@slickgrid-universal/excel-export": "^0.14.1",
"@slickgrid-universal/graphql": "^0.14.1",
"@slickgrid-universal/odata": "^0.14.1",
"@slickgrid-universal/rxjs-observable": "^0.14.1",
"@slickgrid-universal/text-export": "^0.14.1",
"@types/bluebird": "^3.5.35",
"@types/dompurify": "^2.2.2",
"@types/i18next-xhr-backend": "^1.4.2",
Expand Down
1 change: 1 addition & 0 deletions src/aurelia-slickgrid/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export class Constants {
'onBeforeExportToTextFile',
'onAfterExportToTextFile',
'onGridStateChanged',
'onHeaderMenuColumnResizeByContent',
'onPaginationChanged',
'onItemAdded',
'onItemDeleted',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EventAggregator } from 'aurelia-event-aggregator';
import 'jest-extended';
import { EventAggregator } from 'aurelia-event-aggregator';
import { BindingEngine, Container } from 'aurelia-framework';
import { of, throwError } from 'rxjs';
import {
Expand Down Expand Up @@ -32,13 +32,11 @@ import {
SortService,
TreeDataService
} from '@slickgrid-universal/common';
import * as aureliaSlickgridUtilities from '../aurelia-slickgrid-utilities';

import { SlickEmptyWarningComponent } from '@slickgrid-universal/empty-warning-component';
import { GraphqlPaginatedResult, GraphqlService, GraphqlServiceApi, GraphqlServiceOption } from '@slickgrid-universal/graphql';
import { TextExportService } from '@slickgrid-universal/text-export';


import { RxJsResourceStub } from '../../../../test/rxjsResourceStub';
import { HttpStub } from '../../../../test/httpClientStub';
import { MockSlickEvent, MockSlickEventHandler } from '../../../../test/mockSlickEvent';
Expand All @@ -47,6 +45,7 @@ import { AureliaUtilService, ContainerService, TranslaterService } from '../../s
import { PubSubService } from '../../services/pubSub.service';
import { ResizerService } from '../../services/resizer.service';
import { AureliaSlickgridCustomElement } from '../aurelia-slickgrid';
import * as aureliaSlickgridUtilities from '../aurelia-slickgrid-utilities';

const mockAutoAddCustomEditorFormatter = jest.fn();

Expand Down Expand Up @@ -123,6 +122,7 @@ const filterServiceStub = {
bindLocalOnSort: jest.fn(),
bindBackendOnSort: jest.fn(),
populateColumnFilterSearchTermPresets: jest.fn(),
refreshTreeDataFilters: jest.fn(),
getColumnFilters: jest.fn(),
} as unknown as FilterService;

Expand Down Expand Up @@ -180,10 +180,11 @@ const sortServiceStub = {

const treeDataServiceStub = {
init: jest.fn(),
convertFlatDatasetConvertToHierarhicalView: jest.fn(),
initializeHierarchicalDataset: jest.fn(),
convertFlatParentChildToTreeDataset: jest.fn(),
convertFlatParentChildToTreeDatasetAndSort: jest.fn(),
dispose: jest.fn(),
handleOnCellClick: jest.fn(),
sortHierarchicalDataset: jest.fn(),
toggleTreeDataCollapse: jest.fn(),
} as unknown as TreeDataService;

Expand Down Expand Up @@ -1822,6 +1823,10 @@ describe('Aurelia-Slickgrid Component instantiated via Constructor', () => {
});

describe('Custom Footer', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should have a Custom Footer when "showCustomFooter" is enabled and there are no Pagination used', (done) => {
const mockColDefs = [{ id: 'name', field: 'name', editor: undefined, internalColumnEditor: {} }];
const mockGridOptions = { enableTranslate: true, showCustomFooter: true, };
Expand Down Expand Up @@ -1938,10 +1943,10 @@ describe('Aurelia-Slickgrid Component instantiated via Constructor', () => {
const expectation = {
startTime: expect.toBeDate(),
endTime: expect.toBeDate(),
itemCount: 0,
itemCount: 2,
totalItemCount: 0
};
jest.spyOn(mockDataView, 'getLength').mockReturnValue(0);
jest.spyOn(mockDataView, 'getLength').mockReturnValue(2);

customElement.gridOptions = { enablePagination: false, showCustomFooter: true };
customElement.initialization(slickEventHandler);
Expand Down Expand Up @@ -2094,57 +2099,114 @@ describe('Aurelia-Slickgrid Component instantiated via Constructor', () => {
});

describe('Tree Data View', () => {
it('should throw an error when enableTreeData is enabled with Pagination since that is not supported', (done) => {
try {
customElement.gridOptions = { enableTreeData: true, enablePagination: true } as GridOption;
customElement.initialization(slickEventHandler);
} catch (e) {
expect(e.toString()).toContain('[Aurelia-Slickgrid] It looks like you are trying to use Tree Data with Pagination but unfortunately that is simply not supported because of its complexity.');
customElement.dispose();
done();
}
afterEach(() => {
customElement.dispose();
jest.clearAllMocks();
});

it('should throw an error when enableTreeData is enabled without passing a "columnId"', (done) => {
try {
customElement.gridOptions = { enableTreeData: true, treeDataOptions: {} } as unknown as GridOption;
customElement.initialization(slickEventHandler);
it('should change flat dataset and expect "convertFlatParentChildToTreeDatasetAndSort" being called with other methods', () => {
const mockFlatDataset = [{ id: 0, file: 'documents' }, { id: 1, file: 'vacation.txt', parentId: 0 }];
const mockHierarchical = [{ id: 0, file: 'documents', files: [{ id: 1, file: 'vacation.txt' }] }];
const hierarchicalSpy = jest.spyOn(SharedService.prototype, 'hierarchicalDataset', 'set');
const treeConvertAndSortSpy = jest.spyOn(treeDataServiceStub, 'convertFlatParentChildToTreeDatasetAndSort').mockReturnValue({ hierarchical: mockHierarchical as any[], flat: mockFlatDataset as any[] });
const refreshTreeSpy = jest.spyOn(filterServiceStub, 'refreshTreeDataFilters');

} catch (e) {
expect(e.toString()).toContain('[Aurelia-Slickgrid] When enabling tree data, you must also provide the "treeDataOption" property in your Grid Options with "childrenPropName" or "parentPropName"');
customElement.dispose();
done();
}
customElement.gridOptions = {
enableTreeData: true, treeDataOptions: {
columnId: 'file', parentPropName: 'parentId', childrenPropName: 'files',
initialSort: { columndId: 'file', direction: 'ASC' }
}
} as unknown as GridOption;
customElement.initialization(slickEventHandler);
customElement.dataset = mockFlatDataset;
customElement.datasetChanged(mockFlatDataset, []);

expect(hierarchicalSpy).toHaveBeenCalledWith(mockHierarchical);
expect(refreshTreeSpy).toHaveBeenCalled();
expect(treeConvertAndSortSpy).toHaveBeenCalled();
});

it('should change flat dataset and expect being called with other methods', () => {
it('should change flat dataset and expect "convertFlatParentChildToTreeDataset" being called (without sorting) and other methods as well', () => {
const mockFlatDataset = [{ id: 0, file: 'documents' }, { id: 1, file: 'vacation.txt', parentId: 0 }];
const mockHierarchical = [{ id: 0, file: 'documents', files: [{ id: 1, file: 'vacation.txt' }] }];
const hierarchicalSpy = jest.spyOn(SharedService.prototype, 'hierarchicalDataset', 'set');
jest.spyOn(treeDataServiceStub, 'initializeHierarchicalDataset').mockReturnValue({ hierarchical: mockHierarchical, flat: mockFlatDataset });
const treeConvertSpy = jest.spyOn(treeDataServiceStub, 'convertFlatParentChildToTreeDataset').mockReturnValue(mockHierarchical as any[]);
const refreshTreeSpy = jest.spyOn(filterServiceStub, 'refreshTreeDataFilters');

customElement.gridOptions = { enableTreeData: true, treeDataOptions: { columnId: 'file', parentPropName: 'parentId', childrenPropName: 'files' } } as unknown as GridOption;
customElement.gridOptions = {
enableTreeData: true, treeDataOptions: {
columnId: 'file', parentPropName: 'parentId', childrenPropName: 'files'
}
} as unknown as GridOption;
customElement.initialization(slickEventHandler);
customElement.dataset = mockFlatDataset;
customElement.datasetChanged(mockFlatDataset, []);

expect(hierarchicalSpy).toHaveBeenCalledWith(mockHierarchical);
expect(refreshTreeSpy).toHaveBeenCalled();
expect(treeConvertSpy).toHaveBeenCalled();
});

it('should change hierarchical dataset and expect processTreeDataInitialSort being called with other methods', () => {
it('should change hierarchical dataset and expect processTreeDataInitialSort being called with other methods', (done) => {
const mockHierarchical = [{ file: 'documents', files: [{ file: 'vacation.txt' }] }];
const hierarchicalSpy = jest.spyOn(SharedService.prototype, 'hierarchicalDataset', 'set');
const clearFilterSpy = jest.spyOn(filterServiceStub, 'clearFilters');
const refreshFilterSpy = jest.spyOn(filterServiceStub, 'refreshTreeDataFilters');
const setItemsSpy = jest.spyOn(mockDataView, 'setItems');
const processSpy = jest.spyOn(sortServiceStub, 'processTreeDataInitialSort');

customElement.gridOptions = { enableTreeData: true, treeDataOptions: { columnId: 'file' } } as unknown as GridOption;
customElement.initialization(slickEventHandler);
customElement.datasetHierarchical = mockHierarchical;
customElement.datasetHierarchicalChanged(mockHierarchical);

expect(hierarchicalSpy).toHaveBeenCalledWith(mockHierarchical);
expect(clearFilterSpy).toHaveBeenCalled();
expect(processSpy).toHaveBeenCalled();
expect(setItemsSpy).toHaveBeenCalledWith([], 'id');
setTimeout(() => {
expect(refreshFilterSpy).toHaveBeenCalled();
done();
});
});

it('should preset hierarchical dataset before the initialization and expect sortHierarchicalDataset to be called', () => {
const mockFlatDataset = [{ id: 0, file: 'documents' }, { id: 1, file: 'vacation.txt', parentId: 0 }];
const mockHierarchical = [{ id: 0, file: 'documents', files: [{ id: 1, file: 'vacation.txt' }] }];
const hierarchicalSpy = jest.spyOn(SharedService.prototype, 'hierarchicalDataset', 'set');
const clearFilterSpy = jest.spyOn(filterServiceStub, 'clearFilters');
const setItemsSpy = jest.spyOn(mockDataView, 'setItems');
const processSpy = jest.spyOn(sortServiceStub, 'processTreeDataInitialSort');
const sortHierarchicalSpy = jest.spyOn(treeDataServiceStub, 'sortHierarchicalDataset').mockReturnValue({ hierarchical: mockHierarchical as any[], flat: mockFlatDataset as any[] });

customElement.dispose();
customElement.gridOptions = { enableTreeData: true, treeDataOptions: { columnId: 'file', initialSort: { columndId: 'file', direction: 'ASC' } } } as unknown as GridOption;
customElement.datasetHierarchical = mockHierarchical;
customElement.datasetHierarchicalChanged(mockHierarchical);
customElement.initialization(slickEventHandler);

expect(hierarchicalSpy).toHaveBeenCalledWith(mockHierarchical);
expect(clearFilterSpy).toHaveBeenCalled();
expect(processSpy).not.toHaveBeenCalled();
expect(setItemsSpy).toHaveBeenCalledWith(mockFlatDataset, 'id');
expect(sortHierarchicalSpy).toHaveBeenCalledWith(mockHierarchical);
});

it('should expect "refreshTreeDataFilters" method to be called when our flat dataset was already set and it just got changed a 2nd time', () => {
const mockFlatDataset = [{ id: 0, file: 'documents' }, { id: 1, file: 'vacation.txt', parentId: 0 }];
const mockHierarchical = [{ id: 0, file: 'documents', files: [{ id: 1, file: 'vacation.txt' }] }];
const hierarchicalSpy = jest.spyOn(SharedService.prototype, 'hierarchicalDataset', 'set');
jest.spyOn(treeDataServiceStub, 'convertFlatParentChildToTreeDatasetAndSort').mockReturnValue({ hierarchical: mockHierarchical as any[], flat: mockFlatDataset as any[] });
const refreshTreeSpy = jest.spyOn(filterServiceStub, 'refreshTreeDataFilters');

customElement.dataset = [{ id: 0, file: 'documents' }];
customElement.gridOptions = { enableTreeData: true, treeDataOptions: { columnId: 'file', parentPropName: 'parentId', childrenPropName: 'files', initialSort: { columndId: 'file', direction: 'ASC' } } } as unknown as GridOption;
customElement.initialization(slickEventHandler);
customElement.dataset = mockFlatDataset;
customElement.datasetChanged(mockFlatDataset, []);

expect(hierarchicalSpy).toHaveBeenCalledWith(mockHierarchical);
expect(refreshTreeSpy).toHaveBeenCalled();
});
});
});
Expand Down
8 changes: 4 additions & 4 deletions src/aurelia-slickgrid/custom-elements/aurelia-slickgrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,9 +411,8 @@ export class AureliaSlickgridCustomElement {
this.grid.init();

if (!this.customDataView && this.dataview) {
this.dataview.beginUpdate();
this.dataview.setItems(this._dataset, this.gridOptions.datasetIdPropertyName);
this.dataview.endUpdate();
const initialDataset = this.gridOptions?.enableTreeData ? this.sortTreeDataset(this.dataset) : this.dataset;
this.dataview.setItems(initialDataset, this.gridOptions.datasetIdPropertyName);

// if you don't want the items that are not visible (due to being filtered out or being on a different page)
// to stay selected, pass 'false' to the second arg
Expand Down Expand Up @@ -639,14 +638,15 @@ export class AureliaSlickgridCustomElement {

datasetChanged(newDataset: any[], oldValue: any[]) {
const prevDatasetLn = this._currentDatasetLength;
let data = [...newDataset];
let data = newDataset;

// when Tree Data is enabled and we don't yet have the hierarchical dataset filled, we can force a convert & sort of the array
if (this.grid && this.gridOptions?.enableTreeData && Array.isArray(newDataset) && (newDataset.length > 0 || newDataset.length !== prevDatasetLn)) {
this._isDatasetHierarchicalInitialized = false;
data = this.sortTreeDataset(newDataset);
}

this._dataset = data;
this.refreshGridData(data || []);
this._currentDatasetLength = newDataset.length;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,14 @@ const mockSelectionModel = jest.fn().mockImplementation(() => ({
destroy: jest.fn()
}));

jest.mock('slickgrid/plugins/slick.rowdetailview', () => mockAddon);
Slick.Plugins = {
RowDetailView: mockAddon
} as any;

jest.mock('slickgrid/plugins/slick.rowselectionmodel', () => mockSelectionModel);
Slick.RowSelectionModel = mockSelectionModel;

describe('rowDetailViewExtension', () => {
jest.mock('slickgrid/plugins/slick.rowdetailview', () => mockAddon);
Slick.Plugins = {
RowDetailView: mockAddon
} as any;
jest.mock('slickgrid/plugins/slick.rowselectionmodel', () => mockSelectionModel);

Slick.RowSelectionModel = mockSelectionModel;
let pluginEa: EventAggregator;
let pubSubService: PubSubService;
let extension: RowDetailViewExtension;
Expand Down
Loading

0 comments on commit 631465a

Please sign in to comment.