Skip to content

Commit

Permalink
fix(menu): context menu to copy cell with queryFieldNameGetterFn (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
ghiscoding authored Jul 20, 2020
1 parent 041087e commit 53c50f9
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 9 deletions.
3 changes: 0 additions & 3 deletions examples/web-demo-vanilla-bundle/src/examples/example50.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,6 @@ export class Example50 {
autoResize: {
container: '.demo-container',
},
contextMenu: {
hideCopyCellValueCommand: true
},
enableAutoSizeColumns: true,
enableAutoResize: true,
enableExport: true,
Expand Down
2 changes: 1 addition & 1 deletion packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"dependencies": {
"dompurify": "^2.0.12",
"flatpickr": "^4.6.3",
"jquery": "~3.4.1",
"jquery": "^3.4.1",
"jquery-ui-dist": "^1.12.1",
"lodash.isequal": "^4.5.0",
"moment-mini": "^2.24.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,54 @@ describe('contextMenuExtension', () => {
expect(execSpy).toHaveBeenCalledWith('copy', false, 'JOHN');
});

it('should call "copyToClipboard" and get the value even when there is a "queryFieldNameGetterFn" callback defined with dot notation the command triggered is "copy"', () => {
const copyGridOptionsMock = { ...gridOptionsMock, enableExcelExport: false, enableExport: false, contextMenu: { hideCopyCellValueCommand: false } } as GridOption;
const columnMock = { id: 'firstName', name: 'First Name', field: 'firstName', queryFieldNameGetterFn: () => 'lastName' } as Column;
const dataContextMock = { id: 123, firstName: 'John', lastName: 'Doe', age: 50 };
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
const execSpy = jest.spyOn(window.document, 'execCommand');
extension.register();
extension.register();

const menuItemCommand = copyGridOptionsMock.contextMenu.commandItems.find((item: MenuCommandItem) => item.command === 'copy') as MenuCommandItem;
menuItemCommand.action(new CustomEvent('change'), {
command: 'copy',
cell: 2,
row: 5,
grid: gridStub,
column: columnMock,
dataContext: dataContextMock,
item: menuItemCommand,
value: 'John'
});

expect(execSpy).toHaveBeenCalledWith('copy', false, 'Doe');
});

it('should call "copyToClipboard" and get the value even when there is a "queryFieldNameGetterFn" callback defined with dot notation the command triggered is "copy"', () => {
const copyGridOptionsMock = { ...gridOptionsMock, enableExcelExport: false, enableExport: false, contextMenu: { hideCopyCellValueCommand: false } } as GridOption;
const columnMock = { id: 'firstName', name: 'First Name', field: 'firstName', queryFieldNameGetterFn: () => 'user.lastName' } as Column;
const dataContextMock = { id: 123, user: { firstName: 'John', lastName: 'Doe', age: 50 } };
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
const execSpy = jest.spyOn(window.document, 'execCommand');
extension.register();
extension.register();

const menuItemCommand = copyGridOptionsMock.contextMenu.commandItems.find((item: MenuCommandItem) => item.command === 'copy') as MenuCommandItem;
menuItemCommand.action(new CustomEvent('change'), {
command: 'copy',
cell: 2,
row: 5,
grid: gridStub,
column: columnMock,
dataContext: dataContextMock,
item: menuItemCommand,
value: 'John'
});

expect(execSpy).toHaveBeenCalledWith('copy', false, 'Doe');
});

it('should expect "itemUsabilityOverride" callback from the "copy" command to return True when a value to copy is found in the dataContext object', () => {
const copyGridOptionsMock = { ...gridOptionsMock, enableExcelExport: false, enableExport: false, contextMenu: { hideCopyCellValueCommand: false } } as GridOption;
const columnMock = { id: 'firstName', name: 'First Name', field: 'firstName' } as Column;
Expand Down Expand Up @@ -691,6 +739,44 @@ describe('contextMenuExtension', () => {
expect(isCommandUsable).toBe(false);
});

it('should expect "itemUsabilityOverride" callback from the "copy" command to return True when there is a "queryFieldNameGetterFn" which itself returns a value', () => {
const copyGridOptionsMock = { ...gridOptionsMock, enableExcelExport: false, enableExport: false, contextMenu: { hideCopyCellValueCommand: false } } as GridOption;
const columnMock = { id: 'firstName', name: 'First Name', field: 'firstName', queryFieldNameGetterFn: () => 'lastName' } as Column;
const dataContextMock = { id: 123, firstName: null, lastName: 'Doe', age: 50 };
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
extension.register();

const menuItemCommand = copyGridOptionsMock.contextMenu.commandItems.find((item: MenuCommandItem) => item.command === 'copy') as MenuCommandItem;
const isCommandUsable = menuItemCommand.itemUsabilityOverride({
cell: 2,
row: 2,
grid: gridStub,
column: columnMock,
dataContext: dataContextMock,
});

expect(isCommandUsable).toBe(true);
});

it('should expect "itemUsabilityOverride" callback from the "copy" command to return True when there is a "queryFieldNameGetterFn" and a dot notation field which does return a value', () => {
const copyGridOptionsMock = { ...gridOptionsMock, enableExcelExport: false, enableExport: false, contextMenu: { hideCopyCellValueCommand: false } } as GridOption;
const columnMock = { id: 'firstName', name: 'First Name', field: 'user.firstName', queryFieldNameGetterFn: () => 'user.lastName' } as Column;
const dataContextMock = { id: 123, user: { firstName: null, lastName: 'Doe', age: 50 } };
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
extension.register();

const menuItemCommand = copyGridOptionsMock.contextMenu.commandItems.find((item: MenuCommandItem) => item.command === 'copy') as MenuCommandItem;
const isCommandUsable = menuItemCommand.itemUsabilityOverride({
cell: 2,
row: 2,
grid: gridStub,
column: columnMock,
dataContext: dataContextMock,
});

expect(isCommandUsable).toBe(true);
});

it('should call "exportToExcel" and expect an error thrown when ExcelExportService is not registered prior to calling the method', () => {
const copyGridOptionsMock = { ...gridOptionsMock, enableExcelExport: true, enableExport: false, contextMenu: { hideCopyCellValueCommand: true, hideExportCsvCommand: true, hideExportExcelCommand: false } } as GridOption;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
Expand Down
40 changes: 36 additions & 4 deletions packages/common/src/extensions/contextMenuExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { DelimiterType, ExtensionName, FileType, } from '../enums/index';
import { ExtensionUtility } from './extensionUtility';
import { exportWithFormatterWhenDefined } from '../services/export-utilities';
import { SharedService } from '../services/shared.service';
import { getTranslationPrefix } from '../services/utilities';
import { getDescendantProperty, getTranslationPrefix } from '../services/utilities';
import { ExcelExportService, FileExportService, TranslaterService, TreeDataService } from '../services/index';

// using external non-typed js libraries
Expand Down Expand Up @@ -197,7 +197,12 @@ export class ContextMenuExtension implements Extension {
// make sure there's an item to copy before enabling this command
const columnDef = args && args.column as Column;
const dataContext = args && args.dataContext;
if (columnDef && dataContext.hasOwnProperty(columnDef.field)) {
if (typeof columnDef.queryFieldNameGetterFn === 'function') {
const cellValue = this.getCellValueFromQueryFieldGetter(columnDef, dataContext);
if (cellValue !== '' && cellValue !== undefined) {
return true;
}
} else if (columnDef && dataContext.hasOwnProperty(columnDef.field)) {
return dataContext[columnDef.field] !== '' && dataContext[columnDef.field] !== null && dataContext[columnDef.field] !== undefined;
}
return false;
Expand Down Expand Up @@ -403,11 +408,15 @@ export class ContextMenuExtension implements Extension {
const gridOptions = this.sharedService && this.sharedService.gridOptions || {};
const cell = args && args.cell || 0;
const row = args && args.row || 0;
const column = args && args.column;
const columnDef = args && args.column;
const dataContext = args && args.dataContext;
const grid = this.sharedService && this.sharedService.grid;
const exportOptions = gridOptions && (gridOptions.excelExportOptions || gridOptions.exportOptions);
const textToCopy = exportWithFormatterWhenDefined(row, cell, dataContext, column, grid, exportOptions);
let textToCopy = exportWithFormatterWhenDefined(row, cell, dataContext, columnDef, grid, exportOptions);

if (typeof columnDef.queryFieldNameGetterFn === 'function') {
textToCopy = this.getCellValueFromQueryFieldGetter(columnDef, dataContext);
}

// create fake <textarea> (positioned outside of the screen) to copy into clipboard & delete it from the DOM once we're done
const tmpElem = document.createElement('textarea') as HTMLTextAreaElement;
Expand All @@ -426,4 +435,27 @@ export class ContextMenuExtension implements Extension {
}
} catch (e) { }
}

/**
* When a queryFieldNameGetterFn is defined, then get the value from that getter callback function
* @param columnDef
* @param dataContext
* @return cellValue
*/
private getCellValueFromQueryFieldGetter(columnDef: Column, dataContext: any): string {
let cellValue = '';

if (typeof columnDef.queryFieldNameGetterFn === 'function') {
const queryFieldName = columnDef.queryFieldNameGetterFn(dataContext);

// get the cell value from the item or when it's a dot notation then exploded the item and get the final value
if (queryFieldName && queryFieldName.indexOf('.') >= 0) {
cellValue = getDescendantProperty(dataContext, queryFieldName);
} else {
cellValue = dataContext[queryFieldName];
}
}

return cellValue;
}
}
Binary file not shown.
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7362,7 +7362,7 @@ jquery-ui@>=1.8.0:
resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.12.1.tgz#bcb4045c8dd0539c134bc1488cdd3e768a7a9e51"
integrity sha1-vLQEXI3QU5wTS8FIjN0+dop6nlE=

jquery@>=1.8.0, jquery@^3.4.1, jquery@~3.4.1:
jquery@>=1.8.0, jquery@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.4.1.tgz#714f1f8d9dde4bdfa55764ba37ef214630d80ef2"
integrity sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==
Expand Down

0 comments on commit 53c50f9

Please sign in to comment.