Skip to content
This repository was archived by the owner on Jun 1, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ module.exports = {
],
reporters: ['default', 'jest-junit'],
setupFiles: ['<rootDir>/jest-pretest.ts'],
setupTestFrameworkScriptFile: '<rootDir>/setup-jest.ts',
setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
transform: {
'^.+\\.(ts|html)$': '<rootDir>/node_modules/jest-preset-angular/preprocessor.js',
},
Expand Down
11 changes: 5 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
"vinyl-paths": "^2.1.0"
},
"devDependencies": {
"@angular-builders/jest": "^7.3.1",
"@angular-builders/jest": "^7.4.2",
"@angular-devkit/build-angular": "~0.13.1",
"@angular/animations": "^7.2.8",
"@angular/cli": "^7.3.1",
Expand All @@ -105,12 +105,12 @@
"@angular/router": "^7.2.8",
"@ng-select/ng-select": "^2.15.3",
"@types/flatpickr": "^3.1.2",
"@types/jest": "^23.3.9",
"@types/jest": "^24.0.12",
"@types/jquery": "^3.3.29",
"@types/moment": "^2.13.0",
"@types/node": "^10.12.15",
"@types/text-encoding-utf-8": "^1.0.1",
"babel-jest": "^23.6.0",
"babel-jest": "^24.8.0",
"bootstrap": "3.4.1",
"codecov": "^3.3.0",
"codelyzer": "~4.5.0",
Expand All @@ -124,15 +124,14 @@
"gulp-bump": "^3.1.3",
"gulp-sass": "^4.0.2",
"gulp-yuidoc": "^0.1.2",
"jest": "^23.6.0",
"jest-junit": "^6.3.0",
"jest": "^24.8.0",
"jest-junit": "^6.4.0",
"jest-preset-angular": "^6.0.1",
"mocha": "^5.2.0",
"mochawesome": "^3.1.2",
"mochawesome-merge": "^1.0.7",
"mochawesome-report-generator": "^3.1.5",
"ng-packagr": "^4.7.0",
"ngx-wallaby-jest": "^0.0.2",
"node-sass": "^4.11.0",
"npm-run-all": "^4.1.5",
"postcss-cli": "^6.0.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
this.resizer.init(grid);
}
if (options.enableAutoResize) {
this.resizer.attachAutoResizeDataGrid();
this.resizer.bindAutoResizeDataGrid();
if (grid && options.autoFitColumnsOnFirstLoad && options.enableAutoSizeColumns) {
grid.autosizeColumns();
}
Expand Down
1 change: 1 addition & 0 deletions src/app/modules/angular-slickgrid/global-grid-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const GlobalGridOptions: GridOption = {
asyncEditorLoading: false,
autoFitColumnsOnFirstLoad: true,
autoResize: {
calculateAvailableSizeBy: 'window',
bottomPadding: 20,
minHeight: 180,
minWidth: 300,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export interface AutoResizeOption {
/** Defaults to 'window', which DOM element are we using to calculate the available size for the grid? */
calculateAvailableSizeBy?: 'container' | 'window';

/** bottom padding of the grid in pixels */
bottomPadding?: number;

Expand Down
190 changes: 190 additions & 0 deletions src/app/modules/angular-slickgrid/services/resizer.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import { GridOption } from './../models/gridOption.interface';
import { ResizerService } from './resizer.service';

const DATAGRID_MIN_HEIGHT = 180;
const DATAGRID_MIN_WIDTH = 300;
const DATAGRID_BOTTOM_PADDING = 20;
const DATAGRID_PAGINATION_HEIGHT = 35;
const gridId = 'grid1';
const gridUid = 'slickgrid_124343';
const containerId = 'demo-container';

const gridOptionMock = {
gridId,
gridContainerId: `slickGridContainer-${gridId}`,
autoResize: { containerId },
enableAutoResize: true
} as GridOption;

const gridStub = {
autosizeColumns: jest.fn(),
getScrollbarDimensions: jest.fn(),
getOptions: () => gridOptionMock,
getUID: () => gridUid,
};

// define a <div> container to simulate the grid container
const template =
`<div id="${containerId}" style="height: 800px; width: 600px; overflow: hidden; display: block;">
<div id="slickGridContainer-${gridId}" class="gridPane" style="width: 100%;">
<div id="${gridId}" class="${gridUid}" style="width: 100%"></div>
</div>
</div>`;

// --- NOTE ---
// with JSDOM our container or element height/width will always be 0 (JSDOM does not render like a real browser)
// we can only mock the window height/width, we cannot mock an element height/width
// I tried various hack but nothing worked, this one for example https://github.com/jsdom/jsdom/issues/135#issuecomment-68191941

describe('Resizer Service', () => {
let service: ResizerService;

beforeEach(() => {
const div = document.createElement('div');
div.innerHTML = template;
document.body.appendChild(div);

service = new ResizerService();
service.init(gridStub);
});

afterEach(() => {
service.dispose();
});

it('should create the service', () => {
expect(service).toBeTruthy();
});

it('should throw an error when there is no grid object defined', () => {
service = new ResizerService();
service.init(null);
expect(() => service.resizeGrid()).toThrowError('Angular-Slickgrid resizer requires a valid Grid object and Grid Options defined');
});

it('should throw an error when there is no grid options defined', () => {
service = new ResizerService();
service.init({ getOptions: () => null });
expect(() => service.resizeGrid()).toThrowError('Angular-Slickgrid resizer requires a valid Grid object and Grid Options defined');
});

it('should trigger a grid resize when a window resize event occurs', () => {
// arrange
const newHeight = 500;
const previousHeight = window.innerHeight;
const subjectBeforeSpy = jest.spyOn(service.onGridBeforeResize, 'next');
const subjectAfterSpy = jest.spyOn(service.onGridAfterResize, 'next');
const gridSpy = jest.spyOn(gridStub, 'getOptions');
const serviceCalculateSpy = jest.spyOn(service, 'calculateGridNewDimensions');
const serviceResizeSpy = jest.spyOn(service, 'resizeGrid');

// act
// bind window resize & call a viewport resize
service.bindAutoResizeDataGrid();
Object.defineProperty(window, 'innerHeight', { writable: true, configurable: true, value: newHeight });
window.dispatchEvent(new Event('resize'));
const lastDimensions = service.getLastResizeDimensions();

// so the height dimension will work because calculateGridNewDimensions() uses "window.innerHeight" while the width it uses the container width
// for that reason, we can only verify the height, while the width should be set as the minimum width from the constant because 0 is override by the constant
const dimensionResult = { height: newHeight - DATAGRID_BOTTOM_PADDING, width: DATAGRID_MIN_WIDTH };

// assert
expect(gridSpy).toHaveBeenCalled();
expect(serviceResizeSpy).toHaveBeenCalled();
expect(window.innerHeight).not.toEqual(previousHeight);
expect(serviceCalculateSpy).toReturnWith(dimensionResult);
expect(lastDimensions).toEqual(dimensionResult);
expect(subjectBeforeSpy).toHaveBeenCalledWith(true);
expect(subjectAfterSpy).toHaveBeenCalledWith(dimensionResult);
});

it('should resize grid to a defined height and width when fixed dimensions are provided to the init method', () => {
const fixedHeight = 330;
const fixedWidth = 412;
const windowHeight = 840;
service.init(gridStub, { height: fixedHeight, width: fixedWidth });
const serviceCalculateSpy = jest.spyOn(service, 'calculateGridNewDimensions');

Object.defineProperty(window, 'innerHeight', { writable: true, configurable: true, value: windowHeight });
window.dispatchEvent(new Event('resize'));
service.calculateGridNewDimensions(gridOptionMock);

// same comment as previous test, the height dimension will work because calculateGridNewDimensions() uses "window.innerHeight"
expect(serviceCalculateSpy).toReturnWith({ height: fixedHeight, width: fixedWidth });
});

it('should calculate new dimensions when calculateGridNewDimensions is called', () => {
const newHeight = 440;
const serviceCalculateSpy = jest.spyOn(service, 'calculateGridNewDimensions');

Object.defineProperty(window, 'innerHeight', { writable: true, configurable: true, value: newHeight });
window.dispatchEvent(new Event('resize'));
service.calculateGridNewDimensions(gridOptionMock);

// same comment as previous test, the height dimension will work because calculateGridNewDimensions() uses "window.innerHeight"
expect(serviceCalculateSpy).toReturnWith({ height: (newHeight - DATAGRID_BOTTOM_PADDING), width: DATAGRID_MIN_WIDTH });
});

it('should calculate new dimensions minus a padding when "bottomPadding" is defined in "autoResize" and calculateGridNewDimensions is called', () => {
const newHeight = 422;
const inputBottomPadding = 13;
const serviceCalculateSpy = jest.spyOn(service, 'calculateGridNewDimensions');

Object.defineProperty(window, 'innerHeight', { writable: true, configurable: true, value: newHeight });
window.dispatchEvent(new Event('resize'));
service.calculateGridNewDimensions({ ...gridOptionMock, autoResize: { bottomPadding: inputBottomPadding } });

// same comment as previous test, the height dimension will work because calculateGridNewDimensions() uses "window.innerHeight"
expect(serviceCalculateSpy).toReturnWith({ height: (newHeight - inputBottomPadding), width: DATAGRID_MIN_WIDTH });
});

it('should calculate new dimensions minus the pagination height when pagination is enabled and resizeGrid is called with a delay', (done) => {
const newHeight = 440;
const newOptions = { ...gridOptionMock, enablePagination: true };
const newGridStub = { ...gridStub, getOptions: () => newOptions };
service.init(newGridStub);
const subjectAfterSpy = jest.spyOn(service.onGridAfterResize, 'next');
const serviceCalculateSpy = jest.spyOn(service, 'calculateGridNewDimensions');

Object.defineProperty(window, 'innerHeight', { writable: true, configurable: true, value: newHeight });
window.dispatchEvent(new Event('resize'));
service.resizeGrid(2).then((newGridDimensions) => {
// same comment as previous test, the height dimension will work because calculateGridNewDimensions() uses "window.innerHeight"
const calculatedDimensions = { height: (newHeight - DATAGRID_BOTTOM_PADDING - DATAGRID_PAGINATION_HEIGHT), width: DATAGRID_MIN_WIDTH };
expect(serviceCalculateSpy).toReturnWith(calculatedDimensions);
expect(newGridDimensions).toEqual({ ...calculatedDimensions, heightWithPagination: (calculatedDimensions.height + DATAGRID_PAGINATION_HEIGHT) });
expect(subjectAfterSpy).toHaveBeenCalledWith(newGridDimensions);
done();
});
});

it('should calculate new dimensions by using the container dimensions (instead of the window dimensions) when calculateAvailableSizeBy is set to container', () => {
const newHeight = 500;
const spy = jest.spyOn(service, 'calculateGridNewDimensions');

Object.defineProperty(window, 'innerHeight', { writable: true, configurable: true, value: newHeight });
window.dispatchEvent(new Event('resize'));
service.calculateGridNewDimensions({ ...gridOptionMock, autoResize: { calculateAvailableSizeBy: 'container' } });

// with JSDOM the height is always 0 so we can assume that the height will be the minimum height (without the padding)
expect(spy).toReturnWith({ height: DATAGRID_MIN_HEIGHT, width: DATAGRID_MIN_WIDTH });
});

it('should call the autosizeColumns from the core lib when "enableAutoSizeColumns" is set and the new width is wider than prior width', () => {
const newHeight = 500;
const newOptions = { ...gridOptionMock, enableAutoSizeColumns: true };
const newGridStub = { ...gridStub, getOptions: () => newOptions };
service.init(newGridStub);
const serviceCalculateSpy = jest.spyOn(service, 'calculateGridNewDimensions');
const gridAutosizeSpy = jest.spyOn(newGridStub, 'autosizeColumns');

service.bindAutoResizeDataGrid();
Object.defineProperty(window, 'innerHeight', { writable: true, configurable: true, value: newHeight });
window.dispatchEvent(new Event('resize'));

// with JSDOM the height is always 0 so we can assume that the height will be the minimum height (without the padding)
expect(serviceCalculateSpy).toHaveBeenCalled();
expect(gridAutosizeSpy).toHaveBeenCalled();
});
});
43 changes: 28 additions & 15 deletions src/app/modules/angular-slickgrid/services/resizer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export class ResizerService {
private _grid: any;
private _lastDimensions: GridDimension;
private _timer: any;
onGridAfterResize = new Subject<GridDimension>();
onGridBeforeResize = new Subject<boolean>();

/** Getter for the Grid Options pulled through the Grid Object */
Expand All @@ -31,7 +32,7 @@ export class ResizerService {
}

private get _gridUid(): string {
return (this._grid && this._grid.getUID) ? this._grid.getUID() : this._gridOptions.gridId;
return (this._grid && this._grid.getUID) ? this._grid.getUID() : this._gridOptions && this._gridOptions.gridId;
}

init(grid: any, fixedDimensions?: GridDimension): void {
Expand All @@ -45,7 +46,7 @@ export class ResizerService {
/** Attach an auto resize trigger on the datagrid, if that is enable then it will resize itself to the available space
* Options: we could also provide a % factor to resize on each height/width independently
*/
attachAutoResizeDataGrid(newSizes?: GridDimension) {
bindAutoResizeDataGrid(newSizes?: GridDimension) {
// if we can't find the grid to resize, return without attaching anything
const gridDomElm = $(`#${this._gridOptions && this._gridOptions.gridId ? this._gridOptions.gridId : 'grid1'}`);
if (gridDomElm === undefined || gridDomElm.offset() === undefined) {
Expand All @@ -70,10 +71,9 @@ export class ResizerService {
*/
calculateGridNewDimensions(gridOptions: GridOption): GridDimension | null {
const gridDomElm = $(`#${gridOptions.gridId}`);
const autoResizeOptions = gridOptions && gridOptions.autoResize;
const autoResizeOptions = gridOptions && gridOptions.autoResize || {};
const containerElm = (autoResizeOptions && autoResizeOptions.containerId) ? $(`#${autoResizeOptions.containerId}`) : $(`#${gridOptions.gridContainerId}`);
const windowElm = $(window);
if (windowElm === undefined || containerElm === undefined || gridDomElm === undefined) {
if (!window || containerElm === undefined || gridDomElm === undefined) {
return null;
}

Expand All @@ -84,9 +84,20 @@ export class ResizerService {
bottomPadding += DATAGRID_PAGINATION_HEIGHT;
}

const gridHeight = windowElm.height() || 0;
const coordOffsetTop = gridDomElm.offset();
const gridOffsetTop = (coordOffsetTop !== undefined) ? coordOffsetTop.top : 0;
let gridHeight = 0;
let gridOffsetTop = 0;

// which DOM element are we using to calculate the available size for the grid?
if (autoResizeOptions.calculateAvailableSizeBy === 'container') {
// uses the container's height to calculate grid height without any top offset
gridHeight = containerElm.height() || 0;
} else {
// uses the browser's window height with its top offset to calculate grid height
gridHeight = window.innerHeight || 0;
const coordOffsetTop = gridDomElm.offset();
gridOffsetTop = (coordOffsetTop !== undefined) ? coordOffsetTop.top : 0;
}

const availableHeight = gridHeight - gridOffsetTop - bottomPadding;
const availableWidth = containerElm.width() || 0;
const maxHeight = autoResizeOptions && autoResizeOptions.maxHeight || undefined;
Expand Down Expand Up @@ -158,7 +169,7 @@ export class ResizerService {
if (!this._grid || !this._gridOptions) {
throw new Error(`
Angular-Slickgrid resizer requires a valid Grid object and Grid Options defined.
You can fix this by setting your gridOption to use "enableAutoResize" or create an instance of the ResizerService by calling attachAutoResizeDataGrid()`);
You can fix this by setting your gridOption to use "enableAutoResize" or create an instance of the ResizerService by calling bindAutoResizeDataGrid()`);
}

return new Promise((resolve) => {
Expand All @@ -167,17 +178,19 @@ export class ResizerService {

if (delay > 0) {
clearTimeout(this._timer);
this._timer = setTimeout(() => {
this.resizeGridWithDimensions(newSizes);
resolve(this._lastDimensions);
}, delay);
this._timer = setTimeout(() => resolve(this.resizeGridCallback(newSizes)), delay);
} else {
this.resizeGridWithDimensions(newSizes);
resolve(this._lastDimensions);
resolve(this.resizeGridCallback(newSizes));
}
});
}

resizeGridCallback(newSizes: GridDimension) {
const lastDimensions = this.resizeGridWithDimensions(newSizes);
this.onGridAfterResize.next(lastDimensions);
return lastDimensions;
}

resizeGridWithDimensions(newSizes?: GridDimension): GridDimension {
// calculate the available sizes with minimum height defined as a constant
const availableDimensions = this.calculateGridNewDimensions(this._gridOptions);
Expand Down