Skip to content

Commit

Permalink
feat(menu): add action & override callbacks to all Menu plugins
Browse files Browse the repository at this point in the history
- the list of menus are HeaderMenu, GridMenu & HeaderButton (with some exceptions)
- add more Cypress tests as well
  • Loading branch information
ghiscoding-SE committed Dec 18, 2019
1 parent d4594d1 commit a7967bb
Show file tree
Hide file tree
Showing 30 changed files with 739 additions and 189 deletions.
2 changes: 1 addition & 1 deletion src/app/examples/grid-contextmenu.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const taskTranslateFormatter: Formatter = (row: number, cell: number, value: any
encapsulation: ViewEncapsulation.None
})
export class GridContextMenuComponent implements OnInit {
title = 'Example 26: Cell Menu / Context Menu';
title = 'Example 26: Cell Menu & Context Menu Plugins';
subTitle = `Add Cell Menu and Context Menu
<ul>
<li>This example demonstrates 2 SlickGrid plugins
Expand Down
14 changes: 7 additions & 7 deletions src/app/examples/grid-headerbutton.component.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<div class="container-fluid">
<h2>{{title}}</h2>
<div class="subtitle" [innerHTML]="subTitle"></div>
<h2>{{title}}</h2>
<div class="subtitle" [innerHTML]="subTitle"></div>

<div class="col-sm-12">
<angular-slickgrid gridId="grid2" (onDataviewCreated)="dataviewReady($event)" (onGridCreated)="gridReady($event)"
[columnDefinitions]="columnDefinitions" [gridOptions]="gridOptions" [dataset]="dataset">
</angular-slickgrid>
</div>
<div class="col-sm-12">
<angular-slickgrid gridId="grid7" (onDataviewCreated)="dataviewReady($event)" (onGridCreated)="gridReady($event)"
[columnDefinitions]="columnDefinitions" [gridOptions]="gridOptions" [dataset]="dataset">
</angular-slickgrid>
</div>
</div>
29 changes: 25 additions & 4 deletions src/app/examples/grid-headerbutton.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ export class GridHeaderButtonComponent implements OnInit, OnDestroy {
<li>Mouse hover the 2nd column to see it's icon/command</li>
<li>For all the other columns, click on top-right red circle icon to enable highlight of negative numbers.</li>
<li>Note: The "Header Button" & "Header Menu" Plugins cannot be used at the same time</li>
<li>Use override callback functions to change the properties of show/hide, enable/disable the menu or certain item(s) from the list</li>
<ol>
<li>These callbacks are: "itemVisibilityOverride", "itemUsabilityOverride"</li>
<li>for example the "Column E" does not show the header button via "itemVisibilityOverride"</li>
<li>for example the "Column J" header button is displayed but it not usable via "itemUsabilityOverride"</li>
<li>... e.g. in the demo, the Context Menu is only available on the first 20 Tasks via "menuUsabilityOverride"</li>
</ol>
</ul>
`;

Expand All @@ -48,6 +55,7 @@ export class GridHeaderButtonComponent implements OnInit, OnDestroy {
enableFiltering: false,
enableCellNavigation: true,
headerButton: {
// you can use the "onCommand" (in Grid Options) and/or the "action" callback (in Column Definition)
onCommand: (e, args) => {
const column = args.column;
const button = args.button;
Expand Down Expand Up @@ -82,20 +90,33 @@ export class GridHeaderButtonComponent implements OnInit, OnDestroy {

getData() {
// Set up some test columns.
for (let i = 0; i < 9; i++) {
for (let i = 0; i < 10; i++) {
this.columnDefinitions.push({
id: i,
name: 'Column' + (i + 1),
name: 'Column ' + String.fromCharCode('A'.charCodeAt(0) + i),
field: i + '',
width: 100, // have the 2 first columns wider
width: i === 0 ? 70 : 100, // have the 2 first columns wider
sortable: true,
formatter: highlightingFormatter,
header: {
buttons: [
{
cssClass: 'fa fa-circle-o red faded',
command: 'toggle-highlight',
tooltip: 'Highlight negative numbers.'
tooltip: 'Highlight negative numbers.',
itemVisibilityOverride: (args) => {
// for example don't show the header button on column "E"
return args.column.name !== 'Column E';
},
itemUsabilityOverride: (args) => {
// for example the button usable everywhere except on last column ='J"
return args.column.name !== 'Column J';
},
action: (e, args) => {
// you can use the "action" callback and/or subscribe to the "onCallback" event, they both have the same arguments
// do something
console.log(`execute a callback action to "${args.command}" on ${args.column.name}`);
}
}
]
}
Expand Down
25 changes: 11 additions & 14 deletions src/app/examples/grid-headermenu.component.html
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
<div class="container-fluid">
<h2>{{title}}</h2>
<div class="subtitle" [innerHTML]="subTitle"></div>
<h2>{{title}}</h2>
<div class="subtitle" [innerHTML]="subTitle"></div>

<button class="btn btn-default btn-sm" (click)="switchLanguage()">
<i class="fa fa-language"></i>
Switch Language
</button>
<button class="btn btn-default btn-sm" (click)="switchLanguage()">
<i class="fa fa-language"></i>
Switch Language
</button>

<div class="col-sm-12">
<angular-slickgrid gridId="grid2"
(onAngularGridCreated)="angularGridReady($event)"
[columnDefinitions]="columnDefinitions"
[gridOptions]="gridOptions"
[dataset]="dataset">
</angular-slickgrid>
</div>
<div class="col-sm-12">
<angular-slickgrid gridId="grid8" (onAngularGridCreated)="angularGridReady($event)"
[columnDefinitions]="columnDefinitions" [gridOptions]="gridOptions" [dataset]="dataset">
</angular-slickgrid>
</div>
</div>
15 changes: 15 additions & 0 deletions src/app/examples/grid-headermenu.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.blue {
color: rgb(73, 73, 255);
}
.orange {
color: orange;
}
.red {
color: red;
}
.bold {
font-weight: bold;
}
.italic {
font-style: italic;
}
56 changes: 42 additions & 14 deletions src/app/examples/grid-headermenu.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Component, OnInit, Injectable } from '@angular/core';
import { AngularGridInstance, Column, ColumnSort, GridOption } from './../modules/angular-slickgrid';
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { AngularGridInstance, Column, GridOption } from './../modules/angular-slickgrid';
import { TranslateService } from '@ngx-translate/core';

@Component({
templateUrl: './grid-headermenu.component.html'
templateUrl: './grid-headermenu.component.html',
styleUrls: ['./grid-headermenu.component.scss'],
encapsulation: ViewEncapsulation.None,
})
export class GridHeaderMenuComponent implements OnInit {
title = 'Example 8: Header Menu Plugin';
Expand All @@ -17,6 +19,12 @@ export class GridHeaderMenuComponent implements OnInit {
<li>Try Sorting (multi-sort) the 2 columns "Duration" and "% Complete" (the other ones are disabled)</li>
<li>Try hiding any columns (you use the "Column Picker" plugin by doing a right+click on the header to show the column back)</li>
<li>Note: The "Header Button" & "Header Menu" Plugins cannot be used at the same time</li>
<li>Use override callback functions to change the properties of show/hide, enable/disable the menu or certain item(s) from the list</li>
<ol>
<li>These callbacks are: "menuUsabilityOverride", "itemVisibilityOverride", "itemUsabilityOverride"</li>
<li>... e.g. in the demo, the "Action" Cell Menu is only available when Priority is set to "High" via "menuUsabilityOverride"</li>
<li>... e.g. in the demo, the Context Menu is only available on the first 20 Tasks via "menuUsabilityOverride"</li>
</ol>
</ul>
`;

Expand All @@ -34,10 +42,10 @@ export class GridHeaderMenuComponent implements OnInit {
this.columnDefinitions = [
{ id: 'title', name: 'Title', field: 'title', headerKey: 'TITLE' },
{ id: 'duration', name: 'Duration', field: 'duration', headerKey: 'DURATION', sortable: true },
{ id: '%', name: '% Complete', field: 'percentComplete', headerKey: 'PERCENT_COMPLETE', sortable: true },
{ id: 'percentComplete', name: '% Complete', field: 'percentComplete', headerKey: 'PERCENT_COMPLETE', sortable: true },
{ id: 'start', name: 'Start', field: 'start', headerKey: 'START' },
{ id: 'finish', name: 'Finish', field: 'finish', headerKey: 'FINISH' },
{ id: 'effort-driven', name: 'Completed', field: 'effortDriven', headerKey: 'COMPLETED' }
{ id: 'completed', name: 'Completed', field: 'completed', headerKey: 'COMPLETED' }
];

this.columnDefinitions.forEach((columnDef) => {
Expand All @@ -50,17 +58,36 @@ export class GridHeaderMenuComponent implements OnInit {
// if you want yours at the bottom then start with 61, below 50 will make your command(s) show on top
{
iconCssClass: 'fa fa-question-circle',
disabled: (columnDef.id === 'effort-driven'), // you can disable a command with certain logic

// you can disable a command with certain logic
// HOWEVER note that if you use "itemUsabilityOverride" has precedence when it is defined
// disabled: (columnDef.id === 'completed'),

titleKey: 'HELP', // use "title" as plain string OR "titleKey" when using a translation key
command: 'help',
positionOrder: 99
tooltip: 'Need assistance?',
cssClass: 'bold', // container css class
textCssClass: (columnDef.id === 'title' || columnDef.id === 'completed') ? '' : 'blue', // just the text css class
positionOrder: 99,
itemUsabilityOverride: (args) => {
// for example if we want to disable the "Help" command over the "Title" column
return !(args.column.id === 'title' || args.column.id === 'completed');
},
itemVisibilityOverride: (args) => {
// for example don't show Help on column "% Complete"
return (args.column.id !== 'percentComplete');
},
action: (e, args) => {
// you can use the "action" callback and/or subscribe to the "onCallback" event, they both have the same arguments
console.log('execute an action on Help', args);
}
},
// you can also add divider between commands (command is a required property but you can set it to empty string)
{
divider: true,
command: '',
positionOrder: 98
},
{ divider: true, command: '', positionOrder: 98 },

// you can use "divider" as a string too, but if you do then make sure it's the correct position in the list
// (since there's no positionOrder when using 'divider')
// 'divider',
]
}
};
Expand All @@ -78,6 +105,7 @@ export class GridHeaderMenuComponent implements OnInit {
headerMenu: {
hideSortCommands: false,
hideColumnHideCommand: false,
// you can use the "onCommand" (in Grid Options) and/or the "action" callback (in Column Definition)
onCommand: (e, args) => {
if (args.command === 'help') {
alert('Please help!!!');
Expand All @@ -94,15 +122,15 @@ export class GridHeaderMenuComponent implements OnInit {
getData() {
// Set up some test columns.
const mockDataset = [];
for (let i = 0; i < 500; i++) {
for (let i = 0; i < 1000; i++) {
mockDataset[i] = {
id: i,
title: 'Task ' + i,
duration: Math.round(Math.random() * 25) + ' days',
percentComplete: Math.round(Math.random() * 100),
start: '01/01/2009',
finish: '01/05/2009',
effortDriven: (i % 5 === 0)
completed: (i % 5 === 0)
};
}
this.dataset = mockDataset;
Expand Down
15 changes: 15 additions & 0 deletions src/app/examples/grid-menu.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.blue {
color: rgb(73, 73, 255);
}
.orange {
color: orange;
}
.red {
color: red;
}
.bold {
font-weight: bold;
}
.italic {
font-style: italic;
}
60 changes: 53 additions & 7 deletions src/app/examples/grid-menu.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Component, OnInit, Injectable } from '@angular/core';
import { Component, OnInit, Injectable, ViewEncapsulation } from '@angular/core';
import { AngularGridInstance, Column, FieldType, Filters, Formatters, GridOption, OperatorType, ExtensionName } from './../modules/angular-slickgrid';
import { TranslateService } from '@ngx-translate/core';

@Component({
templateUrl: './grid-menu.component.html'
templateUrl: './grid-menu.component.html',
styleUrls: ['./grid-menu.component.scss'],
encapsulation: ViewEncapsulation.None,
})
@Injectable()
export class GridMenuComponent implements OnInit {
Expand Down Expand Up @@ -76,6 +78,10 @@ export class GridMenuComponent implements OnInit {
enableFiltering: true,
enableCellNavigation: true,
gridMenu: {
menuUsabilityOverride: (args) => {
// we could disable the menu entirely by returning false
return true;
},
// all titles optionally support translation keys, if you wish to use that feature then use the title properties with the 'Key' suffix (e.g: titleKey)
// example "customTitle" for a plain string OR "customTitleKey" to use a translation key
customTitleKey: 'CUSTOM_COMMANDS',
Expand All @@ -95,13 +101,43 @@ export class GridMenuComponent implements OnInit {
titleKey: 'HELP',
disabled: false,
command: 'help',
positionOrder: 99
positionOrder: 90,
cssClass: 'bold', // container css class
textCssClass: 'blue' // just the text css class
},
// you can also add divider between commands (command is a required property but you can set it to empty string)
// you can pass divider as a string or an object with a boolean (if sorting by position, then use the object)
// note you should use the "divider" string only when items array is already sorted and positionOrder are not specified
{ divider: true, command: '', positionOrder: 89 },
// 'divider',
{
divider: true,
command: '',
positionOrder: 98
title: 'Command 1',
command: 'command1',
positionOrder: 91,
cssClass: 'orange',
// you can use the "action" callback and/or use "onCallback" callback from the grid options, they both have the same arguments
action: (e, args) => alert(args.command),
itemUsabilityOverride: (args) => {
// for example disable the command if there's any filter entered
if (this.angularGrid) {
return this.isObjectEmpty(this.angularGrid.filterService.getColumnFilters());
}
return true;
},
},
{
title: 'Command 2',
command: 'command2',
positionOrder: 92,
cssClass: 'red', // container css class
textCssClass: 'italic', // just the text css class
action: (e, args) => alert(args.command),
itemVisibilityOverride: (args) => {
// for example hide this command from the menu if there's any filter entered
if (this.angularGrid) {
return this.isObjectEmpty(this.angularGrid.filterService.getColumnFilters());
}
return true;
},
},
{
title: 'Disabled command',
Expand All @@ -110,6 +146,7 @@ export class GridMenuComponent implements OnInit {
positionOrder: 98
}
],
// you can use the "action" callback and/or use "onCallback" callback from the grid options, they both have the same arguments
onCommand: (e, args) => {
if (args.command === 'help') {
alert('Please help!!!');
Expand Down Expand Up @@ -167,4 +204,13 @@ export class GridMenuComponent implements OnInit {
gridMenuInstance.showGridMenu(e);
}
}

private isObjectEmpty(obj) {
for (const key in obj) {
if (obj.hasOwnProperty(key) && obj[key] !== '') {
return false;
}
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { TestBed } from '@angular/core/testing';
import { ElementRef } from '@angular/core';
import { TranslateService, TranslateModule } from '@ngx-translate/core';
import { of, Subject, throwError } from 'rxjs';

import { AngularSlickgridComponent } from '../angular-slickgrid.component';
import { ExtensionUtility } from '../../extensions';
Expand All @@ -18,13 +20,10 @@ import {
SharedService,
SortService,
} from '../../services';
import { GridOption, CurrentFilter, CurrentSorter, GridStateType, Pagination, GridState, Column } from '../../models';
import { Column, CurrentFilter, CurrentSorter, GridOption, GridState, GridStateChange, GridStateType, Pagination } from '../../models';
import { Filters } from '../../filters';
import { Editors } from '../../editors';
import * as utilities from '../../services/backend-utilities';
import { of, Subject, throwError } from 'rxjs';
import { GridStateChange } from 'dist/public_api';
import { TestBed } from '@angular/core/testing';


const mockExecuteBackendProcess = jest.fn();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ExtensionName,
MenuCommandItem,
MenuCommandItemCallbackArgs,
MenuOnBeforeMenuShowArgs,
MenuOptionItemCallbackArgs,
MenuOptionItem,
Locale,
Expand Down Expand Up @@ -97,7 +98,7 @@ export class CellMenuExtension implements Extension {
this.sharedService.gridOptions.cellMenu.onOptionSelected(event, args);
}
});
this._eventHandler.subscribe(this._addon.onBeforeMenuShow, (event: Event, args: { cell: number; row: number; grid: any; }) => {
this._eventHandler.subscribe(this._addon.onBeforeMenuShow, (event: Event, args: MenuOnBeforeMenuShowArgs) => {
if (this.sharedService.gridOptions.cellMenu && typeof this.sharedService.gridOptions.cellMenu.onBeforeMenuShow === 'function') {
this.sharedService.gridOptions.cellMenu.onBeforeMenuShow(event, args);
}
Expand Down

0 comments on commit a7967bb

Please sign in to comment.