Skip to content

Commit 417d6b8

Browse files
authored
fix: custom command/column & menu titles should work, fixes #2023 (#2031)
* fix: custom command/column & menu titles should work, fixes #2023 * fix: columnPicker should use custom or translated titles and commands
1 parent aa00f24 commit 417d6b8

File tree

10 files changed

+223
-70
lines changed

10 files changed

+223
-70
lines changed

packages/common/src/extensions/__tests__/slickColumnPicker.spec.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,9 @@ describe('ColumnPickerControl', () => {
467467
.mockReturnValue(undefined as any)
468468
.mockReturnValue(1);
469469

470+
gridOptionsMock.columnPicker!.columnTitle = '';
471+
gridOptionsMock.columnPicker!.forceFitTitle = '';
472+
gridOptionsMock.columnPicker!.syncResizeTitle = '';
470473
gridOptionsMock.columnPicker!.hideForceFitButton = false;
471474
gridOptionsMock.syncColumnCellResize = true;
472475
gridOptionsMock.forceFitColumns = true;
@@ -496,5 +499,49 @@ describe('ColumnPickerControl', () => {
496499
expect(control.getAllColumns()).toEqual(columnsMock);
497500
expect(control.getVisibleColumns()).toEqual(columnsMock);
498501
});
502+
503+
it('should not translate when providing custom titles', () => {
504+
const handlerSpy = vi.spyOn(control.eventHandler, 'subscribe');
505+
const utilitySpy = vi.spyOn(extensionUtility, 'getPickerTitleOutputString');
506+
const translateSpy = vi.spyOn(extensionUtility, 'translateItems');
507+
vi.spyOn(gridStub, 'getColumnIndex')
508+
.mockReturnValue(undefined as any)
509+
.mockReturnValue(1);
510+
511+
translateService.use('fr');
512+
gridOptionsMock.columnPicker!.columnTitle = 'Custom Column Title';
513+
gridOptionsMock.columnPicker!.forceFitTitle = 'Custom Force Fit Title';
514+
gridOptionsMock.columnPicker!.syncResizeTitle = 'Custom Sync Resize Title';
515+
gridOptionsMock.columnPicker!.hideForceFitButton = false;
516+
gridOptionsMock.syncColumnCellResize = true;
517+
gridOptionsMock.forceFitColumns = true;
518+
control.columns = columnsMock;
519+
control.init();
520+
control.translateColumnPicker();
521+
522+
gridStub.onHeaderContextMenu.notify({ column: columnsMock[1], grid: gridStub }, eventData as any, gridStub);
523+
control.menuElement!.querySelector<HTMLInputElement>('input[type="checkbox"]')!.dispatchEvent(new Event('click', { bubbles: true }));
524+
const columnTitleElm = control.menuElement!.querySelector('.slickgrid_124343 .slick-menu-title') as HTMLSpanElement;
525+
const labelForcefitElm = control.menuElement!.querySelector('label[for=slickgrid_124343-colpicker-forcefit]') as HTMLDivElement;
526+
const labelSyncElm = control.menuElement!.querySelector('label[for=slickgrid_124343-colpicker-syncresize]') as HTMLDivElement;
527+
528+
expect(handlerSpy).toHaveBeenCalledTimes(4);
529+
expect(columnTitleElm.textContent).toBe('Custom Column Title');
530+
expect(labelForcefitElm.textContent).toBe('Custom Force Fit Title');
531+
expect(labelSyncElm.textContent).toBe('Custom Sync Resize Title');
532+
expect(utilitySpy).toHaveBeenCalled();
533+
expect(translateSpy).toHaveBeenCalled();
534+
expect((SharedService.prototype.gridOptions.columnPicker as ColumnPicker).columnTitle).toBe('Custom Column Title');
535+
expect((SharedService.prototype.gridOptions.columnPicker as ColumnPicker).forceFitTitle).toBe('Custom Force Fit Title');
536+
expect((SharedService.prototype.gridOptions.columnPicker as ColumnPicker).syncResizeTitle).toBe('Custom Sync Resize Title');
537+
expect(columnsMock).toEqual([
538+
{ id: 'field1', field: 'field1', name: 'Titre', width: 100, nameKey: 'TITLE' },
539+
{ id: 'field2', field: 'field2', name: 'Field 2', width: 75, columnPickerLabel: 'Custom Label' },
540+
{ id: 'field3', field: 'field3', name: 'Field 3', columnGroup: 'Billing', width: 75 },
541+
{ id: 'field4', field: 'field4', name: 'Field 4', width: 75, excludeFromColumnPicker: true },
542+
]);
543+
expect(control.getAllColumns()).toEqual(columnsMock);
544+
expect(control.getVisibleColumns()).toEqual(columnsMock);
545+
});
499546
});
500547
});

packages/common/src/extensions/__tests__/slickGridMenu.spec.ts

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ describe('GridMenuControl', () => {
103103
let translateService: TranslateServiceStub;
104104
let sharedService: SharedService;
105105

106-
const gridMenuOptionsMock = {
106+
const gridMenuOptionsMock: GridMenu = {
107107
commandLabels: {
108108
clearAllFiltersCommandKey: 'CLEAR_ALL_FILTERS',
109109
clearAllSortingCommandKey: 'CLEAR_ALL_SORTING',
@@ -1156,6 +1156,7 @@ describe('GridMenuControl', () => {
11561156
control.init();
11571157
expect(SharedService.prototype.gridOptions.gridMenu!.commandItems).toEqual([
11581158
{
1159+
_orgTitle: '',
11591160
iconCssClass: 'mdi mdi-pin-off-outline',
11601161
titleKey: 'CLEAR_PINNING',
11611162
title: 'Dégeler les colonnes/rangées',
@@ -1175,6 +1176,7 @@ describe('GridMenuControl', () => {
11751176
control.init(); // calling 2x register to make sure it doesn't duplicate commands
11761177
expect(SharedService.prototype.gridOptions.gridMenu!.commandItems).toEqual([
11771178
{
1179+
_orgTitle: '',
11781180
iconCssClass: 'mdi mdi-filter-remove-outline',
11791181
titleKey: 'CLEAR_ALL_FILTERS',
11801182
title: 'Supprimer tous les filtres',
@@ -1183,6 +1185,7 @@ describe('GridMenuControl', () => {
11831185
positionOrder: 50,
11841186
},
11851187
{
1188+
_orgTitle: '',
11861189
iconCssClass: 'mdi mdi-flip-vertical',
11871190
titleKey: 'TOGGLE_FILTER_ROW',
11881191
title: 'Basculer la ligne des filtres',
@@ -1191,6 +1194,7 @@ describe('GridMenuControl', () => {
11911194
positionOrder: 53,
11921195
},
11931196
{
1197+
_orgTitle: '',
11941198
iconCssClass: 'mdi mdi-sync',
11951199
titleKey: 'REFRESH_DATASET',
11961200
title: 'Rafraîchir les données',
@@ -1221,6 +1225,7 @@ describe('GridMenuControl', () => {
12211225
control.init(); // calling 2x register to make sure it doesn't duplicate commands
12221226
expect(SharedService.prototype.gridOptions.gridMenu!.commandItems).toEqual([
12231227
{
1228+
_orgTitle: '',
12241229
iconCssClass: 'mdi mdi-filter-remove-outline',
12251230
titleKey: 'CLEAR_ALL_FILTERS',
12261231
title: 'Supprimer tous les filtres',
@@ -1251,6 +1256,7 @@ describe('GridMenuControl', () => {
12511256
control.init(); // calling 2x register to make sure it doesn't duplicate commands
12521257
expect(SharedService.prototype.gridOptions.gridMenu!.commandItems).toEqual([
12531258
{
1259+
_orgTitle: '',
12541260
iconCssClass: 'mdi mdi-flip-vertical',
12551261
titleKey: 'TOGGLE_FILTER_ROW',
12561262
title: 'Basculer la ligne des filtres',
@@ -1280,6 +1286,7 @@ describe('GridMenuControl', () => {
12801286
control.init(); // calling 2x register to make sure it doesn't duplicate commands
12811287
expect(SharedService.prototype.gridOptions.gridMenu!.commandItems).toEqual([
12821288
{
1289+
_orgTitle: '',
12831290
iconCssClass: 'mdi mdi-brightness-4',
12841291
titleKey: 'TOGGLE_DARK_MODE',
12851292
title: 'Basculer le mode clair/sombre',
@@ -1310,6 +1317,7 @@ describe('GridMenuControl', () => {
13101317
control.init(); // calling 2x register to make sure it doesn't duplicate commands
13111318
expect(SharedService.prototype.gridOptions.gridMenu!.commandItems).toEqual([
13121319
{
1320+
_orgTitle: '',
13131321
iconCssClass: 'mdi mdi-sync',
13141322
titleKey: 'REFRESH_DATASET',
13151323
title: 'Rafraîchir les données',
@@ -1329,6 +1337,7 @@ describe('GridMenuControl', () => {
13291337
control.init(); // calling 2x register to make sure it doesn't duplicate commands
13301338
expect(SharedService.prototype.gridOptions.gridMenu!.commandItems).toEqual([
13311339
{
1340+
_orgTitle: '',
13321341
iconCssClass: 'mdi mdi-flip-vertical',
13331342
titleKey: 'TOGGLE_PRE_HEADER_ROW',
13341343
title: 'Basculer la ligne de pré-en-tête',
@@ -1367,6 +1376,7 @@ describe('GridMenuControl', () => {
13671376
control.init(); // calling 2x register to make sure it doesn't duplicate commands
13681377
expect(SharedService.prototype.gridOptions.gridMenu!.commandItems).toEqual([
13691378
{
1379+
_orgTitle: '',
13701380
iconCssClass: 'mdi mdi-sort-variant-off',
13711381
titleKey: 'CLEAR_ALL_SORTING',
13721382
title: 'Supprimer tous les tris',
@@ -1415,6 +1425,7 @@ describe('GridMenuControl', () => {
14151425
control.init(); // calling 2x register to make sure it doesn't duplicate commands
14161426
expect(SharedService.prototype.gridOptions.gridMenu!.commandItems).toEqual([
14171427
{
1428+
_orgTitle: '',
14181429
iconCssClass: 'mdi mdi-download',
14191430
titleKey: 'EXPORT_TO_CSV',
14201431
title: 'Exporter en format CSV',
@@ -1466,6 +1477,7 @@ describe('GridMenuControl', () => {
14661477
control.init(); // calling 2x register to make sure it doesn't duplicate commands
14671478
expect(SharedService.prototype.gridOptions.gridMenu!.commandItems).toEqual([
14681479
{
1480+
_orgTitle: '',
14691481
iconCssClass: 'mdi mdi-file-excel-outline text-success',
14701482
titleKey: 'EXPORT_TO_EXCEL',
14711483
title: 'Exporter vers Excel',
@@ -1495,6 +1507,7 @@ describe('GridMenuControl', () => {
14951507
control.init(); // calling 2x register to make sure it doesn't duplicate commands
14961508
expect(SharedService.prototype.gridOptions.gridMenu!.commandItems).toEqual([
14971509
{
1510+
_orgTitle: '',
14981511
iconCssClass: 'mdi mdi-download',
14991512
titleKey: 'EXPORT_TO_TAB_DELIMITED',
15001513
title: 'Exporter en format texte (délimité par tabulation)',
@@ -1843,6 +1856,10 @@ describe('GridMenuControl', () => {
18431856
.mockReturnValue(1);
18441857

18451858
translateService.use('fr');
1859+
gridOptionsMock.gridMenu!.commandTitle = '';
1860+
gridOptionsMock.gridMenu!.columnTitle = '';
1861+
gridOptionsMock.gridMenu!.forceFitTitle = '';
1862+
gridOptionsMock.gridMenu!.syncResizeTitle = '';
18461863
gridOptionsMock.gridMenu!.hideForceFitButton = false;
18471864
gridOptionsMock.gridMenu!.hideSyncResizeButton = false;
18481865
gridOptionsMock.syncColumnCellResize = true;
@@ -1852,8 +1869,8 @@ describe('GridMenuControl', () => {
18521869

18531870
control.columns = columnsMock;
18541871
control.initEventHandlers();
1855-
control.translateGridMenu();
18561872
control.init();
1873+
control.translateGridMenu();
18571874
const buttonElm = document.querySelector('.slick-grid-menu-button') as HTMLDivElement;
18581875
buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false }));
18591876
control.menuElement!.querySelector('input[type="checkbox"]')!.dispatchEvent(new Event('click', { bubbles: true }));
@@ -1876,5 +1893,57 @@ describe('GridMenuControl', () => {
18761893
expect(control.getAllColumns()).toEqual(columnsMock);
18771894
expect(control.getVisibleColumns()).toEqual(columnsMock);
18781895
});
1896+
1897+
it('should not translate when providing custom titles', () => {
1898+
const handlerSpy = vi.spyOn(control.eventHandler, 'subscribe');
1899+
const utilitySpy = vi.spyOn(extensionUtility, 'getPickerTitleOutputString');
1900+
const translateSpy = vi.spyOn(extensionUtility, 'translateItems');
1901+
vi.spyOn(gridStub, 'getColumnIndex')
1902+
.mockReturnValue(undefined as any)
1903+
.mockReturnValue(1);
1904+
1905+
translateService.use('fr');
1906+
gridOptionsMock.gridMenu!.commandTitle = 'Custom Command Title';
1907+
gridOptionsMock.gridMenu!.columnTitle = 'Custom Column Title';
1908+
gridOptionsMock.gridMenu!.forceFitTitle = 'Custom Force Fit Title';
1909+
gridOptionsMock.gridMenu!.syncResizeTitle = 'Custom Sync Resize Title';
1910+
gridOptionsMock.gridMenu!.hideForceFitButton = false;
1911+
gridOptionsMock.gridMenu!.hideSyncResizeButton = false;
1912+
gridOptionsMock.syncColumnCellResize = true;
1913+
gridOptionsMock.forceFitColumns = true;
1914+
vi.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
1915+
vi.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock);
1916+
1917+
control.columns = columnsMock;
1918+
control.initEventHandlers();
1919+
control.init();
1920+
control.translateGridMenu();
1921+
const buttonElm = document.querySelector('.slick-grid-menu-button') as HTMLDivElement;
1922+
buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false }));
1923+
control.menuElement!.querySelector('input[type="checkbox"]')!.dispatchEvent(new Event('click', { bubbles: true }));
1924+
// const commandTitleElm = control.menuElement!.querySelectorAll('.slickgrid_124343 .slick-menu-command-list .slick-menu-title')[0] as HTMLSpanElement;
1925+
const columnTitleElm = control.menuElement!.querySelector('.slickgrid_124343 .slick-menu-title') as HTMLSpanElement;
1926+
const labelForcefitElm = control.menuElement!.querySelector('label[for=slickgrid_124343-gridmenu-colpicker-forcefit]') as HTMLLabelElement;
1927+
const labelSyncElm = control.menuElement!.querySelector('label[for=slickgrid_124343-gridmenu-colpicker-syncresize]') as HTMLLabelElement;
1928+
1929+
expect(handlerSpy).toHaveBeenCalledTimes(4);
1930+
// expect(commandTitleElm.textContent).toBe('Custom Command Title');
1931+
expect(columnTitleElm.textContent).toBe('Custom Column Title');
1932+
expect(labelForcefitElm.textContent).toBe('Custom Force Fit Title');
1933+
expect(labelSyncElm.textContent).toBe('Custom Sync Resize Title');
1934+
expect(utilitySpy).toHaveBeenCalled();
1935+
expect(translateSpy).toHaveBeenCalled();
1936+
// expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).commandTitle).toBe('Custom Command Title');
1937+
expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).columnTitle).toBe('Custom Column Title');
1938+
expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).forceFitTitle).toBe('Custom Force Fit Title');
1939+
expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).syncResizeTitle).toBe('Custom Sync Resize Title');
1940+
expect(columnsMock).toEqual([
1941+
{ id: 'field1', field: 'field1', name: 'Titre', width: 100, nameKey: 'TITLE' },
1942+
{ id: 'field2', field: 'field2', name: 'Field 2', width: 75 },
1943+
{ id: 'field3', field: 'field3', name: 'Field 3', columnGroup: 'Billing', width: 75, excludeFromGridMenu: true },
1944+
]);
1945+
expect(control.getAllColumns()).toEqual(columnsMock);
1946+
expect(control.getVisibleColumns()).toEqual(columnsMock);
1947+
});
18791948
});
18801949
});

packages/common/src/extensions/extensionUtility.ts

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -27,56 +27,30 @@ export class ExtensionUtility {
2727

2828
let output = '';
2929
const picker = this.sharedService.gridOptions?.[pickerName] ?? {};
30-
const enableTranslate = this.sharedService.gridOptions?.enableTranslate ?? false;
31-
32-
// get locales provided by user in forRoot or else use default English locales via the Constants
33-
const locales = this.sharedService.gridOptions?.locales ?? Constants.locales;
34-
3530
const title = (picker as any)?.[propName];
3631
const titleKey = (picker as any)?.[`${propName}Key`];
3732
const gridOptions = this.sharedService.gridOptions;
3833
const translationPrefix = getTranslationPrefix(gridOptions);
3934

40-
if (titleKey && this.translaterService?.translate) {
35+
if (!title && titleKey && this.translaterService?.translate) {
4136
output = this.translaterService.translate(titleKey || ' ');
4237
} else {
38+
let transKey = '';
4339
switch (propName) {
4440
case 'commandTitle':
45-
output =
46-
title ||
47-
(enableTranslate &&
48-
this.translaterService?.getCurrentLanguage &&
49-
this.translaterService?.translate(`${translationPrefix}COMMANDS`)) ||
50-
locales?.TEXT_COMMANDS;
41+
transKey = 'COMMANDS';
5142
break;
5243
case 'columnTitle':
53-
output =
54-
title ||
55-
(enableTranslate &&
56-
this.translaterService?.getCurrentLanguage &&
57-
this.translaterService?.translate(`${translationPrefix}COLUMNS`)) ||
58-
locales?.TEXT_COLUMNS;
44+
transKey = 'COLUMNS';
5945
break;
6046
case 'forceFitTitle':
61-
output =
62-
title ||
63-
(enableTranslate &&
64-
this.translaterService?.getCurrentLanguage &&
65-
this.translaterService?.translate(`${translationPrefix}FORCE_FIT_COLUMNS`)) ||
66-
locales?.TEXT_FORCE_FIT_COLUMNS;
47+
transKey = 'FORCE_FIT_COLUMNS';
6748
break;
6849
case 'syncResizeTitle':
69-
output =
70-
title ||
71-
(enableTranslate &&
72-
this.translaterService?.getCurrentLanguage &&
73-
this.translaterService?.translate(`${translationPrefix}SYNCHRONOUS_RESIZE`)) ||
74-
locales?.TEXT_SYNCHRONOUS_RESIZE;
75-
break;
76-
default:
77-
output = title;
50+
transKey = 'SYNCHRONOUS_RESIZE';
7851
break;
7952
}
53+
output = transKey ? this.translateWhenEnabledAndServiceExist(`${translationPrefix}${transKey}`, `TEXT_${transKey}`, title) : title;
8054
}
8155
return output;
8256
}
@@ -165,7 +139,7 @@ export class ExtensionUtility {
165139
// translate `titleKey` and also `subMenuTitleKey` if exists
166140
if (typeof item === 'object') {
167141
if (item.titleKey) {
168-
item.title = this.translateWhenEnabledAndServiceExist(`${item.titleKey}`, `TEXT_${item.titleKey}`);
142+
item.title = this.translateWhenEnabledAndServiceExist(`${item.titleKey}`, `TEXT_${item.titleKey}`, item._orgTitle);
169143
}
170144
if (item.subMenuTitleKey) {
171145
item.subMenuTitle = this.translateWhenEnabledAndServiceExist(`${item.subMenuTitleKey}`, `TEXT_${item.subMenuTitleKey}`);

0 commit comments

Comments
 (0)