Skip to content

Commit

Permalink
feat(resizer): add single Column Resize by Content dblClick & headerMenu
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
ghiscoding committed May 18, 2021
1 parent 2deb3e9 commit 683389f
Show file tree
Hide file tree
Showing 25 changed files with 449 additions and 194 deletions.
3 changes: 2 additions & 1 deletion examples/webpack-demo-vanilla-bundle/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"CLONE": "Clone",
"COLLAPSE_ALL_GROUPS": "Collapse all Groups",
"COLUMNS": "Columns",
"COLUMN_RESIZE_BY_CONTENT": "Resize by Content",
"COMMANDS": "Commands",
"CONTAINS": "Contains",
"COPY": "Copy",
Expand Down Expand Up @@ -93,4 +94,4 @@
"TASK_X": "Task {{x}}",
"TITLE": "Title",
"TRUE": "True"
}
}
3 changes: 2 additions & 1 deletion examples/webpack-demo-vanilla-bundle/assets/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"CLONE": "Cloner",
"COLLAPSE_ALL_GROUPS": "Réduire tous les groupes",
"COLUMNS": "Colonnes",
"COLUMN_RESIZE_BY_CONTENT": "Redimensionner par contenu",
"COMMANDS": "Commandes",
"CONTAINS": "Contient",
"COPY": "Copier",
Expand Down Expand Up @@ -94,4 +95,4 @@
"TITLE": "Titre",
"TITLE.NAME": "Nom du Titre",
"TRUE": "Vrai"
}
}
49 changes: 25 additions & 24 deletions examples/webpack-demo-vanilla-bundle/src/examples/example14.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,14 @@ const customEditableInputFormatter: Formatter = (_row, _cell, value, columnDef,
export class Example14 {
private _bindingEventService: BindingEventService;
columnDefinitions: Column[];
gridOptions1: GridOption;
gridOptions: GridOption;
dataset: any[] = [];
isGridEditable = true;
classDefaultResizeButton = 'button is-small';
classNewResizeButton = 'button is-small is-selected is-primary';
editQueue = [];
editedItems = {};
sgb1: SlickVanillaGridBundle;
sgb: SlickVanillaGridBundle;
gridContainerElm: HTMLDivElement;
loadingClass = '';
complexityLevelList = [
Expand All @@ -99,7 +99,7 @@ export class Example14 {
];

get slickerGridInstance(): SlickerGridInstance {
return this.sgb1?.instances;
return this.sgb?.instances;
}

constructor() {
Expand All @@ -111,7 +111,7 @@ export class Example14 {
this.dataset = this.loadData(NB_ITEMS);
this.gridContainerElm = document.querySelector<HTMLDivElement>(`.grid1`);

this.sgb1 = new Slicker.GridBundle(this.gridContainerElm, Utilities.deepCopy(this.columnDefinitions), { ...ExampleGridOptions, ...this.gridOptions1 }, this.dataset);
this.sgb = new Slicker.GridBundle(this.gridContainerElm, Utilities.deepCopy(this.columnDefinitions), { ...ExampleGridOptions, ...this.gridOptions }, this.dataset);

// bind any of the grid events
this._bindingEventService.bind(this.gridContainerElm, 'onvalidationerror', this.handleValidationError.bind(this));
Expand All @@ -123,7 +123,7 @@ export class Example14 {
}

dispose() {
this.sgb1?.dispose();
this.sgb?.dispose();
this._bindingEventService.unbindAll();
this.gridContainerElm = null;
}
Expand Down Expand Up @@ -343,7 +343,7 @@ export class Example14 {
},
];

this.gridOptions1 = {
this.gridOptions = {
useSalesforceDefaultGridOptions: true,
datasetIdPropertyName: 'id',
eventNamingStyle: EventNamingStyle.lowerCase,
Expand All @@ -364,8 +364,9 @@ export class Example14 {
// then enable resize by content with these 2 flags
autosizeColumnsByCellContentOnFirstLoad: true,
enableAutoResizeColumnsByCellContent: true,
resizeFormatterPaddingWidthInPx: 8, // optional editor formatter padding for resize calculation

resizeByContentOptions: {
formatterPaddingWidthInPx: 8, // optional editor formatter padding for resize calculation
},
enableExcelExport: true,
excelExportOptions: {
exportWithFormatter: false
Expand Down Expand Up @@ -399,8 +400,8 @@ export class Example14 {

if (prevSerializedValue !== serializedValue || serializedValue === '') {
const finalColumn = Array.isArray(editCommand.prevSerializedValue) ? editorColumns[index] : column;
this.editedItems[this.gridOptions1.datasetIdPropertyName || 'id'] = item; // keep items by their row indexes, if the row got edited twice then we'll keep only the last change
this.sgb1.slickGrid.invalidate();
this.editedItems[this.gridOptions.datasetIdPropertyName || 'id'] = item; // keep items by their row indexes, if the row got edited twice then we'll keep only the last change
this.sgb.slickGrid.invalidate();
editCommand.execute();

this.renderUnsavedCellStyling(item, finalColumn, editCommand);
Expand Down Expand Up @@ -508,7 +509,7 @@ export class Example14 {
// when the field "completed" changes to false, we also need to blank out the "finish" date
if (dataContext && !dataContext.completed) {
dataContext.finish = null;
this.sgb1.gridService.updateItem(dataContext);
this.sgb.gridService.updateItem(dataContext);
}
}

Expand All @@ -519,18 +520,18 @@ export class Example14 {

handleDefaultResizeColumns() {
// just for demo purposes, set it back to its original width
const columns = this.sgb1.slickGrid.getColumns();
const columns = this.sgb.slickGrid.getColumns();
columns.forEach(col => col.width = col.originalWidth);
this.sgb1.slickGrid.setColumns(columns);
this.sgb1.slickGrid.autosizeColumns();
this.sgb.slickGrid.setColumns(columns);
this.sgb.slickGrid.autosizeColumns();

// simple css class to change selected button in the UI
this.classDefaultResizeButton = 'button is-small is-selected is-primary';
this.classNewResizeButton = 'button is-small';
}

handleNewResizeColumns() {
this.sgb1.resizerService.resizeColumnsByCellContent(true);
this.sgb.resizerService.resizeColumnsByCellContent(true);

// simple css class to change selected button in the UI
this.classDefaultResizeButton = 'button is-small';
Expand All @@ -543,17 +544,17 @@ export class Example14 {

// then change a single grid options to make the grid non-editable (readonly)
this.isGridEditable = !this.isGridEditable;
this.sgb1.gridOptions = { editable: this.isGridEditable };
this.gridOptions1 = this.sgb1.gridOptions;
this.sgb.gridOptions = { editable: this.isGridEditable };
this.gridOptions = this.sgb.gridOptions;

// we can request a resize of the columns widths by their cell content (we need to pass `true` to request a recalc)
// also another reason to do it here is because we use an extra editable formatter that has its own padding
this.sgb1.resizerService.resizeColumnsByCellContent(true);
this.sgb.resizerService.resizeColumnsByCellContent(true);
}

removeUnsavedStylingFromCell(_item: any, column: Column, row: number) {
// remove unsaved css class from that cell
this.sgb1.slickGrid.removeCellCssStyles(`unsaved_highlight_${[column.id]}${row}`);
this.sgb.slickGrid.removeCellCssStyles(`unsaved_highlight_${[column.id]}${row}`);
}

removeAllUnsavedStylingFromCell() {
Expand Down Expand Up @@ -583,10 +584,10 @@ export class Example14 {

renderUnsavedCellStyling(item, column, editCommand) {
if (editCommand && item && column) {
const row = this.sgb1.dataView.getRowByItem(item);
const row = this.sgb.dataView.getRowByItem(item);
if (row >= 0) {
const hash = { [row]: { [column.id]: 'unsaved-editable-field' } };
this.sgb1.slickGrid.setCellCssStyles(`unsaved_highlight_${[column.id]}${row}`, hash);
this.sgb.slickGrid.setCellCssStyles(`unsaved_highlight_${[column.id]}${row}`, hash);
}
}
}
Expand Down Expand Up @@ -618,12 +619,12 @@ export class Example14 {
for (const lastEditColumn of lastEdit.columns) {
this.removeUnsavedStylingFromCell(lastEdit.item, lastEditColumn, lastEditCommand.row);
}
this.sgb1.slickGrid.invalidate();
this.sgb.slickGrid.invalidate();


// optionally open the last cell editor associated
if (showLastEditor) {
this.sgb1?.slickGrid.gotoCell(lastEditCommand.row, lastEditCommand.cell, false);
this.sgb?.slickGrid.gotoCell(lastEditCommand.row, lastEditCommand.cell, false);
}
}
}
Expand All @@ -640,7 +641,7 @@ export class Example14 {
}
}
}
this.sgb1.slickGrid.invalidate(); // re-render the grid only after every cells got rolled back
this.sgb.slickGrid.invalidate(); // re-render the grid only after every cells got rolled back
this.editQueue = [];
}

Expand Down
2 changes: 2 additions & 0 deletions examples/webpack-demo-vanilla-bundle/src/examples/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export class Icons {
'.mdi.mdi-arrow-down-bold-box-outline',
'.mdi.mdi-arrow-down-bold-outline',
'.mdi.mdi-arrow-expand',
'.mdi.mdi-arrow-expand-horizontal',
'.mdi.mdi-arrow-split-vertical',
'.mdi.mdi-calendar',
'.mdi.mdi-calendar-check',
'.mdi.mdi-calendar-clock',
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class Constants {
TEXT_COLLAPSE_ALL_GROUPS: 'Collapse all Groups',
TEXT_CONTAINS: 'Contains',
TEXT_COLUMNS: 'Columns',
TEXT_COLUMN_RESIZE_BY_CONTENT: 'Resize by Content',
TEXT_COMMANDS: 'Commands',
TEXT_COPY: 'Copy',
TEXT_EQUALS: 'Equals',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ describe('headerMenuExtension', () => {

const gridOptionsMock = {
enableAutoSizeColumns: true,
enableColumnResizeOnDoubleClick: true,
enableHeaderMenu: true,
enableTranslate: true,
backendServiceApi: {
Expand All @@ -82,6 +83,7 @@ describe('headerMenuExtension', () => {
},
headerMenu: {
hideFreezeColumnsCommand: false,
hideColumnResizeByContentCommand: false,
hideForceFitButton: false,
hideSyncResizeButton: true,
onExtensionRegistered: jest.fn(),
Expand Down Expand Up @@ -140,6 +142,7 @@ describe('headerMenuExtension', () => {
autoAlignOffset: 12,
minWidth: 140,
hideFreezeColumnsCommand: false,
hideColumnResizeByContentCommand: false,
hideColumnHideCommand: false,
hideForceFitButton: false,
hideSyncResizeButton: true,
Expand Down Expand Up @@ -239,14 +242,28 @@ describe('headerMenuExtension', () => {

expect(mockColumn.header.menu.items).not.toBeNull();
expect(mockColumn.header.menu.items).toEqual([
{ iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 48 },
{ iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 47 },
{ iconCssClass: 'fa fa-arrows-h', title: 'Redimensionner par contenu', command: 'column-resize-by-content', positionOrder: 48 },
{ divider: true, command: '', positionOrder: 49 },
{ iconCssClass: 'fa fa-times', title: 'Cacher la colonne', command: 'hide', positionOrder: 55 }
]);
});

it('should have the commands "column-resize-by-content" and "hide" in the header menu list', () => {
const copyGridOptionsMock = { ...gridOptionsMock, headerMenu: { hideFreezeColumnsCommand: true, hideColumnResizeByContentCommand: false } } as unknown as GridOption;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
extension.register();

expect(mockColumn.header.menu.items).not.toBeNull();
expect(mockColumn.header.menu.items).toEqual([
{ iconCssClass: 'fa fa-arrows-h', title: 'Redimensionner par contenu', command: 'column-resize-by-content', positionOrder: 48 },
{ divider: true, command: '', positionOrder: 49 },
{ iconCssClass: 'fa fa-times', title: 'Cacher la colonne', command: 'hide', positionOrder: 55 }
]);
});

it('should have the command "hide-column" in the header menu list', () => {
const copyGridOptionsMock = { ...gridOptionsMock, headerMenu: { hideFreezeColumnsCommand: true, hideColumnHideCommand: false } } as unknown as GridOption;
const copyGridOptionsMock = { ...gridOptionsMock, headerMenu: { hideFreezeColumnsCommand: true, hideColumnResizeByContentCommand: true, hideColumnHideCommand: false } } as unknown as GridOption;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
extension.register();

Expand All @@ -257,7 +274,7 @@ describe('headerMenuExtension', () => {
});

it('should expect all menu related to Sorting when "enableSorting" is set', () => {
const copyGridOptionsMock = { ...gridOptionsMock, enableSorting: true, headerMenu: { hideFreezeColumnsCommand: true, hideColumnHideCommand: true } } as unknown as GridOption;
const copyGridOptionsMock = { ...gridOptionsMock, enableSorting: true, headerMenu: { hideFreezeColumnsCommand: true, hideColumnHideCommand: true, hideColumnResizeByContentCommand: true } } as unknown as GridOption;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);

extension.register();
Expand All @@ -277,7 +294,7 @@ describe('headerMenuExtension', () => {
});

it('should expect only the "hide-column" command in the menu when "enableSorting" and "hideSortCommands" are set', () => {
const copyGridOptionsMock = { ...gridOptionsMock, enableSorting: true } as unknown as GridOption;
const copyGridOptionsMock = { ...gridOptionsMock, enableSorting: true, enableColumnResizeOnDoubleClick: false } as unknown as GridOption;
copyGridOptionsMock.headerMenu!.hideColumnHideCommand = false;
copyGridOptionsMock.headerMenu!.hideSortCommands = true;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
Expand All @@ -286,14 +303,14 @@ describe('headerMenuExtension', () => {

expect(mockColumn.header.menu.items).not.toBeNull();
expect(mockColumn.header.menu.items).toEqual([
{ iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 48 },
{ iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 47 },
{ divider: true, command: '', positionOrder: 49 },
{ iconCssClass: 'fa fa-times', title: 'Cacher la colonne', command: 'hide', positionOrder: 55 }
]);
});

it('should expect all menu related to Filtering when "enableFiltering" is set', () => {
const copyGridOptionsMock = { ...gridOptionsMock, enableFiltering: true, headerMenu: { hideFreezeColumnsCommand: true, hideColumnHideCommand: true } } as unknown as GridOption;
const copyGridOptionsMock = { ...gridOptionsMock, enableFiltering: true, headerMenu: { hideFreezeColumnsCommand: true, hideColumnHideCommand: true, hideColumnResizeByContentCommand: true, } } as unknown as GridOption;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);

extension.register();
Expand Down Expand Up @@ -321,7 +338,8 @@ describe('headerMenuExtension', () => {
header: {
menu: {
items: [
{ iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 48 },
{ iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 47 },
{ iconCssClass: 'fa fa-arrows-h', title: 'Redimensionner par contenu', command: 'column-resize-by-content', positionOrder: 48 },
{ divider: true, command: '', positionOrder: 49 },
{ command: 'hide', iconCssClass: 'fa fa-times', positionOrder: 55, title: 'Cacher la colonne' }
]
Expand Down Expand Up @@ -349,7 +367,8 @@ describe('headerMenuExtension', () => {
header: {
menu: {
items: [
{ iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 48 },
{ iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 47 },
{ iconCssClass: 'fa fa-arrows-h', title: 'Redimensionner par contenu', command: 'column-resize-by-content', positionOrder: 48 },
{ divider: true, command: '', positionOrder: 49 },
{ command: 'hide', iconCssClass: 'fa fa-times', positionOrder: 55, title: 'Cacher la colonne' }
]
Expand All @@ -372,7 +391,8 @@ describe('headerMenuExtension', () => {
header: {
menu: {
items: [
{ iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 48 },
{ iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 47 },
{ iconCssClass: 'fa fa-arrows-h', title: 'Redimensionner par contenu', command: 'column-resize-by-content', positionOrder: 48 },
{ divider: true, command: '', positionOrder: 49 },
{ iconCssClass: 'fa fa-sort-asc', title: 'Trier par ordre croissant', command: 'sort-asc', positionOrder: 50 },
{ iconCssClass: 'fa fa-sort-desc', title: 'Trier par ordre décroissant', command: 'sort-desc', positionOrder: 51 },
Expand All @@ -394,7 +414,8 @@ describe('headerMenuExtension', () => {
header: {
menu: {
items: [
{ iconCssClass: 'fa fa-thumb-tack', title: 'Freeze Columns', command: 'freeze-columns', positionOrder: 48 },
{ iconCssClass: 'fa fa-thumb-tack', title: 'Freeze Columns', command: 'freeze-columns', positionOrder: 47 },
{ iconCssClass: 'fa fa-arrows-h', title: 'Resize by Content', command: 'column-resize-by-content', positionOrder: 48 },
{ divider: true, command: '', positionOrder: 49 },
{ iconCssClass: 'fa fa-sort-asc', title: 'Sort Ascending', command: 'sort-asc', positionOrder: 50 },
{ iconCssClass: 'fa fa-sort-desc', title: 'Sort Descending', command: 'sort-desc', positionOrder: 51 },
Expand All @@ -414,6 +435,19 @@ describe('headerMenuExtension', () => {
jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock);
});

it('should trigger the command "column-resize-by-content" and grid "setOptions" method to be called with current column position', () => {
const setOptionsSpy = jest.spyOn(gridStub, 'setOptions');
const setColumnsSpy = jest.spyOn(gridStub, 'setColumns');
const pubsubSpy = jest.spyOn(pubSubServiceStub, 'publish');
const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu as HeaderMenu, 'onCommand');

const instance = extension.register() as SlickHeaderMenu;
instance.onCommand!.notify({ column: columnsMock[0], grid: gridStub, command: 'column-resize-by-content', item: { command: 'column-resize-by-content' } }, new Slick.EventData(), gridStub);

expect(onCommandSpy).toHaveBeenCalled();
expect(pubsubSpy).toHaveBeenCalledWith('onHeaderMenuColumnResizeByContent', { columnId: 'field1' });
});

it('should trigger the command "freeze-columns" and grid "setOptions" method to be called with current column position', () => {
const setOptionsSpy = jest.spyOn(gridStub, 'setOptions');
const setColumnsSpy = jest.spyOn(gridStub, 'setColumns');
Expand Down
Loading

0 comments on commit 683389f

Please sign in to comment.