Skip to content

Commit 5604669

Browse files
committed
feat(footer): add row selection count to the footer component
1 parent e0b092e commit 5604669

14 files changed

+156
-30
lines changed

src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid-constructor.spec.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ const mockGrid = {
229229
setSelectedRows: jest.fn(),
230230
onRendered: jest.fn(),
231231
onScroll: jest.fn(),
232-
onDataviewCreated: new Slick.Event(),
232+
onSelectedRowsChanged: new Slick.Event(),
233233
};
234234

235235
const mockSlickCoreImplementation = jest.fn().mockImplementation(() => (mockSlickCore));
@@ -1441,6 +1441,7 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
14411441
expect(component.showCustomFooter).toBeTrue();
14421442
expect(component.customFooterOptions).toEqual({
14431443
dateFormat: 'yyyy-MM-dd, hh:mm aaaaa\'m\'',
1444+
hideRowSelectionCount: false,
14441445
hideLastUpdateTimestamp: true,
14451446
hideTotalItemCount: false,
14461447
footerHeight: 20,
@@ -1449,6 +1450,8 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
14491450
metricTexts: {
14501451
items: 'items',
14511452
itemsKey: 'ITEMS',
1453+
itemsSelected: 'items selected',
1454+
itemsSelectedKey: 'ITEMS_SELECTED',
14521455
of: 'of',
14531456
ofKey: 'OF',
14541457
},
@@ -1479,6 +1482,7 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
14791482
expect(component.showCustomFooter).toBeTrue();
14801483
expect(component.customFooterOptions).toEqual({
14811484
dateFormat: 'yyyy-MM-dd, hh:mm aaaaa\'m\'',
1485+
hideRowSelectionCount: false,
14821486
hideLastUpdateTimestamp: true,
14831487
hideTotalItemCount: false,
14841488
footerHeight: 20,
@@ -1487,6 +1491,8 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
14871491
metricTexts: {
14881492
items: 'some items',
14891493
itemsKey: 'ITEMS',
1494+
itemsSelected: 'items selected',
1495+
itemsSelectedKey: 'ITEMS_SELECTED',
14901496
lastUpdate: 'some last update',
14911497
of: 'some of',
14921498
ofKey: 'OF',

src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid.component.spec.ts

+74
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ import {
4545
} from '../../extensions';
4646
import * as utilities from '../../services/utilities';
4747

48+
declare const Slick: any;
49+
50+
function removeExtraSpaces(text: string) {
51+
return `${text}`.replace(/\s{2,}/g, '');
52+
}
53+
4854
const mockConvertParentChildArray = jest.fn();
4955
// @ts-ignore
5056
utilities.convertParentChildArrayToHierarchicalView = mockConvertParentChildArray;
@@ -306,4 +312,72 @@ describe('App Component', () => {
306312
}, 10);
307313
});
308314
});
315+
316+
describe('Custom Footer', () => {
317+
it('should display 1 items selected on the left side footer section after triggering "onSelectedRowsChanged" event', () => {
318+
const mockColDefs = [{ id: 'name', field: 'name', editor: undefined, internalColumnEditor: {} }];
319+
320+
component.gridId = 'grid1';
321+
component.gridOptions = {
322+
enablePagination: false,
323+
showCustomFooter: true,
324+
enableCheckboxSelector: true,
325+
customFooterOptions: {
326+
hideRowSelectionCount: false
327+
}
328+
} as GridOption;
329+
fixture.detectChanges();
330+
component.columnDefinitions = mockColDefs;
331+
component.grid.setSelectionModel(new Slick.CellSelectionModel());
332+
component.grid.onSelectedRowsChanged.notify({ rows: [1], previousSelectedRows: [] });
333+
fixture.detectChanges();
334+
335+
const gridContainerElm = document.querySelector('.slickgrid-container') as HTMLDivElement;
336+
const gridPaneElm = document.querySelector('.gridPane') as HTMLDivElement;
337+
const footerContainerElm = document.querySelector('div.slick-custom-footer') as HTMLDivElement;
338+
const leftFooterElm = document.querySelector('div.slick-custom-footer > div.left-footer') as HTMLSpanElement;
339+
const rightFooterElm = document.querySelector('div.slick-custom-footer > div.metrics') as HTMLSpanElement;
340+
341+
expect(gridPaneElm.id).toBe('slickGridContainer-grid1');
342+
expect(gridContainerElm.id).toBe('grid1');
343+
expect(footerContainerElm).toBeTruthy();
344+
expect(rightFooterElm).toBeTruthy();
345+
expect(leftFooterElm).toBeTruthy();
346+
expect(component.gridOptions.customFooterOptions!.leftFooterText).toBe('1 items selected');
347+
expect(leftFooterElm.innerHTML).toContain('1 items selected');
348+
});
349+
350+
it('should not not display row selection count after triggering "onSelectedRowsChanged" event when "hideRowSelectionCount" is set to True', () => {
351+
const mockColDefs = [{ id: 'name', field: 'name', editor: undefined, internalColumnEditor: {} }];
352+
353+
component.gridId = 'grid1';
354+
component.gridOptions = {
355+
enablePagination: false,
356+
showCustomFooter: true,
357+
enableCheckboxSelector: true,
358+
customFooterOptions: {
359+
hideRowSelectionCount: true
360+
}
361+
} as GridOption;
362+
fixture.detectChanges();
363+
component.columnDefinitions = mockColDefs;
364+
component.grid.setSelectionModel(new Slick.CellSelectionModel());
365+
component.grid.onSelectedRowsChanged.notify({ rows: [1], previousSelectedRows: [] });
366+
fixture.detectChanges();
367+
368+
const gridContainerElm = document.querySelector('.slickgrid-container') as HTMLDivElement;
369+
const gridPaneElm = document.querySelector('.gridPane') as HTMLDivElement;
370+
const footerContainerElm = document.querySelector('div.slick-custom-footer') as HTMLDivElement;
371+
const leftFooterElm = document.querySelector('div.slick-custom-footer > div.left-footer') as HTMLSpanElement;
372+
const rightFooterElm = document.querySelector('div.slick-custom-footer > div.metrics') as HTMLSpanElement;
373+
374+
expect(gridPaneElm.id).toBe('slickGridContainer-grid1');
375+
expect(gridContainerElm.id).toBe('grid1');
376+
expect(footerContainerElm).toBeTruthy();
377+
expect(rightFooterElm).toBeTruthy();
378+
expect(leftFooterElm).toBeTruthy();
379+
expect(component.gridOptions.customFooterOptions!.leftFooterText).toBeFalsy();
380+
expect(leftFooterElm.innerHTML).toBeFalsy();
381+
});
382+
});
309383
});

src/app/modules/angular-slickgrid/components/__tests__/slick-pagination.component.spec.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,15 @@ describe('App Component', () => {
5757
translate.setTranslation('fr', {
5858
ITEMS: 'éléments',
5959
ITEMS_PER_PAGE: 'éléments par page',
60+
ITEMS_SELECTED: 'éléments sélectionnés',
6061
OF: 'de',
6162
PAGE: 'Page',
6263
PAGE_X_OF_Y: 'page {{x}} de {{y}}',
6364
});
6465
translate.setTranslation('en', {
6566
ITEMS: 'items',
6667
ITEMS_PER_PAGE: 'items per page',
68+
ITEMS_SELECTED: 'items selected',
6769
OF: 'of',
6870
PAGE: 'Page',
6971
PAGE_X_OF_Y: 'page {{x}} of {{y}}',
@@ -114,7 +116,7 @@ describe('App Component', () => {
114116
component.gridOptions = { enableTranslate: true } as GridOption;
115117
fixture.detectChanges();
116118

117-
const elm = document.querySelector('.slick-pagination');
119+
const elm = document.querySelector('.slick-pagination') as HTMLDivElement;
118120
const pageInfoFromTo = fixture.debugElement.query(By.css('.page-info-from-to')).nativeElement;
119121
const pageInfoTotalItems = fixture.debugElement.query(By.css('.page-info-total-items')).nativeElement;
120122

@@ -132,15 +134,15 @@ describe('App Component', () => {
132134

133135
expect(selectElement.value).toBe('10');
134136
expect(selectElement.selectedIndex).toBe(1);
135-
const optionElement = selectElement.selectedOptions.item(0);
137+
const optionElement = selectElement.selectedOptions.item(0) as HTMLOptionElement;
136138
expect(optionElement.value).toBe('10');
137139
});
138140

139141
it('should create a the Slick-Pagination component in the DOM and expect different locale when changed', () => {
140142
translate.use('en');
141143
fixture.detectChanges();
142144

143-
const elm = document.querySelector('.slick-pagination');
145+
const elm = document.querySelector('.slick-pagination') as HTMLDivElement;
144146
const pageInfoFromTo = fixture.debugElement.query(By.css('.page-info-from-to')).nativeElement;
145147
const pageInfoTotalItems = fixture.debugElement.query(By.css('.page-info-total-items')).nativeElement;
146148

src/app/modules/angular-slickgrid/components/angular-slickgrid.component.html

+1-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@
99
<!-- Custom Footer section under the grid -->
1010
<div *ngIf="showCustomFooter && customFooterOptions" class="slick-custom-footer" style="width: 100%;"
1111
[style.height]="customFooterOptions?.footerHeight || 20">
12-
<div class="left-footer" [ngClass]="customFooterOptions.leftContainerClass">
13-
{{customFooterOptions.leftFooterText}}
14-
</div>
12+
<div class="left-footer" [ngClass]="customFooterOptions.leftContainerClass">{{customFooterOptions.leftFooterText}}</div>
1513

1614
<div class="right-footer metrics" [ngClass]="customFooterOptions.rightContainerClass"
1715
*ngIf="metrics && !customFooterOptions.hideMetrics">

src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts

+19
Original file line numberDiff line numberDiff line change
@@ -1178,8 +1178,10 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
11781178
customFooterOptions.metricTexts = customFooterOptions.metricTexts || {};
11791179
customFooterOptions.metricTexts.lastUpdate = customFooterOptions.metricTexts.lastUpdate || this.locales && this.locales.TEXT_LAST_UPDATE || 'TEXT_LAST_UPDATE';
11801180
customFooterOptions.metricTexts.items = customFooterOptions.metricTexts.items || this.locales && this.locales.TEXT_ITEMS || 'TEXT_ITEMS';
1181+
customFooterOptions.metricTexts.itemsSelected = customFooterOptions.metricTexts.itemsSelected || this.locales && this.locales.TEXT_ITEMS_SELECTED || 'TEXT_ITEMS_SELECTED';
11811182
customFooterOptions.metricTexts.of = customFooterOptions.metricTexts.of || this.locales && this.locales.TEXT_OF || 'TEXT_OF';
11821183
}
1184+
this.registerOnSelectedRowsChangedWhenEnabled(this.gridOptions.customFooterOptions);
11831185

11841186
// we will display the custom footer only when there's no Pagination
11851187
if (!this.gridOptions.enablePagination && !this._isPaginationInitialized) {
@@ -1190,6 +1192,23 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
11901192
}
11911193
}
11921194

1195+
/**
1196+
* When user has row selections enabled and does not have any custom text shown on the left side footer,
1197+
* we will show the row selection count on the bottom left side of the footer (by subscribing to the SlickGrid `onSelectedRowsChanged` event).
1198+
* @param customFooterOptions
1199+
*/
1200+
private registerOnSelectedRowsChangedWhenEnabled(customFooterOptions?: CustomFooterOption) {
1201+
const isRowSelectionEnabled = this.gridOptions.enableCheckboxSelector || this.gridOptions.enableRowSelection;
1202+
if (isRowSelectionEnabled && customFooterOptions && (!customFooterOptions.hideRowSelectionCount && !customFooterOptions.leftFooterText)) {
1203+
const selectedCountText = customFooterOptions.metricTexts?.itemsSelected ?? this.locales?.TEXT_ITEMS_SELECTED ?? 'TEXT_ITEMS_SELECTED';
1204+
customFooterOptions.leftFooterText = `0 ${selectedCountText}`;
1205+
1206+
this._eventHandler.subscribe(this.grid.onSelectedRowsChanged, (_e: any, args: { rows: number[]; previousSelectedRows: number[]; }) => {
1207+
customFooterOptions.leftFooterText = `${args.rows.length || 0} ${selectedCountText}`;
1208+
});
1209+
}
1210+
}
1211+
11931212
private treeDataSortComparer(flatDataset: any[]): any[] {
11941213
const dataViewIdIdentifier = this.gridOptions && this.gridOptions.datasetIdPropertyName || 'id';
11951214
const treeDataOpt: TreeDataOption = this.gridOptions && this.gridOptions.treeDataOptions || { columnId: '' };

src/app/modules/angular-slickgrid/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export class Constants {
2929
TEXT_HIDE_COLUMN: 'Hide Column',
3030
TEXT_ITEMS: 'items',
3131
TEXT_ITEMS_PER_PAGE: 'items per page',
32+
TEXT_ITEMS_SELECTED: 'items selected',
3233
TEXT_OF: 'of',
3334
TEXT_OK: 'OK',
3435
TEXT_LAST_UPDATE: 'Last Update',

src/app/modules/angular-slickgrid/global-grid-options.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export const GlobalGridOptions: Partial<GridOption> = {
5353
},
5454
customFooterOptions: {
5555
dateFormat: 'yyyy-MM-dd, hh:mm aaaaa\'m\'',
56+
hideRowSelectionCount: false,
5657
hideTotalItemCount: false,
5758
hideLastUpdateTimestamp: true,
5859
footerHeight: 20,
@@ -61,9 +62,11 @@ export const GlobalGridOptions: Partial<GridOption> = {
6162
metricSeparator: '|',
6263
metricTexts: {
6364
items: 'items',
64-
of: 'of',
6565
itemsKey: 'ITEMS',
66+
of: 'of',
6667
ofKey: 'OF',
68+
itemsSelected: 'items selected',
69+
itemsSelectedKey: 'ITEMS_SELECTED'
6770
}
6871
},
6972
dataView: {

src/app/modules/angular-slickgrid/models/customFooterOption.interface.ts

+9-21
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { MetricTexts } from './metricTexts.interface';
2+
13
export interface CustomFooterOption {
24
/** Optionally pass some text to be displayed on the left side (in the "left-footer" css class) */
35
leftFooterText?: string;
@@ -11,6 +13,12 @@ export interface CustomFooterOption {
1113
/** Defaults to 20, height of the Custom Footer in pixels (this is required and is used by the auto-resizer) */
1214
footerHeight?: number;
1315

16+
/**
17+
* Defaults to false, which will hide the selected rows count on the bottom left of the footer.
18+
* NOTE: if users defined a `leftFooterText`, then the selected rows count will NOT show up.
19+
*/
20+
hideRowSelectionCount?: boolean;
21+
1422
/** Defaults to false, do we want to hide the last update timestamp (endTime)? */
1523
hideLastUpdateTimestamp?: boolean;
1624

@@ -27,27 +35,7 @@ export interface CustomFooterOption {
2735
metricSeparator?: string;
2836

2937
/** Text shown in the custom footer on the far right for the metrics */
30-
metricTexts?: {
31-
/** Defaults to empty string, optionally pass a text (Last Update) to display before the metrics endTime timestamp. */
32-
lastUpdate?: string;
33-
34-
/** Defaults to "items", word to display at the end of the metrics to represent the items (e.g. you could change it for "users" or anything else). */
35-
items?: string;
36-
37-
/** Defaults to "of", text word separator to display between the filtered items count and the total unfiltered items count (e.g.: "10 of 100 items"). */
38-
of?: string;
39-
40-
// -- Translation Keys --//
41-
42-
/** Defaults to "ITEMS", translation key used for the word displayed at the end of the metrics to represent the items (e.g. you could change it for "users" or anything else). */
43-
itemsKey?: string;
44-
45-
/** Defaults to empty string, optionally pass a translation key (internally we use "LAST_UPDATE") to display before the metrics endTime timestamp. */
46-
lastUpdateKey?: string;
47-
48-
/** Defaults to "OF", translation key used for the to display between the filtered items count and the total unfiltered items count. */
49-
ofKey?: string;
50-
};
38+
metricTexts?: MetricTexts;
5139

5240
/** CSS class used for the right container */
5341
rightContainerClass?: string;

src/app/modules/angular-slickgrid/models/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export * from './menuItem.interface';
116116
export * from './menuOptionItem.interface';
117117
export * from './menuOptionItemCallbackArgs.interface';
118118
export * from './metrics.interface';
119+
export * from './metricTexts.interface';
119120
export * from './multiColumnSort.interface';
120121
export * from './multipleSelectOption.interface';
121122
export * from './odataOption.interface';

src/app/modules/angular-slickgrid/models/locale.interface.ts

+3
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ export interface Locale {
8080
/** Text "items per page" displayed in the Pagination (when enabled) */
8181
TEXT_ITEMS_PER_PAGE?: string;
8282

83+
/** Text "Records Selected" displayed in the Custom Footer */
84+
TEXT_ITEMS_SELECTED?: string;
85+
8386
/** Text "Last Update" displayed in the Footer (when enabled) */
8487
TEXT_LAST_UPDATE?: string;
8588

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
export type MetricTexts = {
2+
/** Defaults to empty string, optionally pass a text (Last Update) to display before the metrics endTime timestamp. */
3+
lastUpdate?: string;
4+
5+
/** Defaults to "items", word to display at the end of the metrics to represent the items (e.g. you could change it for "users" or anything else). */
6+
items?: string;
7+
8+
/** Defaults to "of", text word separator to display between the filtered items count and the total unfiltered items count (e.g.: "10 of 100 items"). */
9+
of?: string;
10+
11+
/** Defaults to "records selected", text word that is associated to the row selection count. */
12+
itemsSelected?: string;
13+
14+
// --
15+
// Translation Keys
16+
// ------------------
17+
18+
/** Defaults to "ITEMS", translation key used for the word displayed at the end of the metrics to represent the items (e.g. you could change it for "users" or anything else). */
19+
itemsKey?: string;
20+
21+
/** Defaults to empty string, optionally pass a translation key (internally we use "LAST_UPDATE") to display before the metrics endTime timestamp. */
22+
lastUpdateKey?: string;
23+
24+
/** Defaults to "OF", translation key used for the to display between the filtered items count and the total unfiltered items count. */
25+
ofKey?: string;
26+
27+
/** Defaults to "ITEMS_SELECTED", text word that is associated to the row selection count. */
28+
itemsSelectedKey?: string;
29+
};

src/app/modules/angular-slickgrid/services/resizer.service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export class ResizerService {
115115

116116
// optionally show a custom footer with the data metrics (dataset length and last updated timestamp)
117117
if (bottomPadding && gridOptions.showCustomFooter) {
118-
bottomPadding += gridOptions.customFooterOptions && gridOptions.customFooterOptions.footerHeight || DATAGRID_FOOTER_HEIGHT;
118+
bottomPadding += gridOptions?.customFooterOptions?.footerHeight ?? DATAGRID_FOOTER_HEIGHT;
119119
}
120120

121121
let gridHeight = 0;

src/assets/i18n/en.json

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"IN_COLLECTION_SEPERATED_BY_COMMA": "Search items in a collection, must be separated by a comma (a,b)",
3131
"ITEMS": "items",
3232
"ITEMS_PER_PAGE": "items per page",
33+
"ITEMS_SELECTED": "items selected",
3334
"LAST_UPDATE": "Last Update",
3435
"LESS_THAN": "Less than",
3536
"LESS_THAN_OR_EQUAL_TO": "Less than or equal to",

src/assets/i18n/fr.json

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"INVALID_FLOAT": "Le nombre doit être valide et avoir un maximum de {{maxDecimal}} décimales.",
3131
"ITEMS": "éléments",
3232
"ITEMS_PER_PAGE": "éléments par page",
33+
"ITEMS_SELECTED": "éléments sélectionnés",
3334
"LAST_UPDATE": "Dernière mise à jour",
3435
"LESS_THAN": "Plus petit que",
3536
"LESS_THAN_OR_EQUAL_TO": "Plus petit ou égal à",

0 commit comments

Comments
 (0)