Skip to content

Commit

Permalink
fix(context): strip hidden special chars on context menu Copy command
Browse files Browse the repository at this point in the history
- there were some hidden characters that were carried over when doing copy+paste, and the only way I found to fix that was to replace `\u034f` (unicode char) with empty string
- we should also trim the string to remove any empty spaces from start/end of string
  • Loading branch information
ghiscoding committed Nov 9, 2021
1 parent c9d3cff commit f94ca83
Show file tree
Hide file tree
Showing 4 changed files with 12 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,7 @@ describe('ContextMenu Plugin', () => {
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, enableTextExport: 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 } };
const dataContextMock = { id: 123, user: { firstName: '\u034f\u034fJohn', lastName: '\u034f\u034f Doe', age: 50 } };
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
const execSpy = jest.spyOn(window.document, 'execCommand');
plugin.dispose();
Expand All @@ -844,7 +844,7 @@ describe('ContextMenu Plugin', () => {
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, enableTextExport: false, contextMenu: { hideCopyCellValueCommand: false } } as GridOption;
const columnMock = { id: 'firstName', name: 'First Name', field: 'firstName' } as Column;
const dataContextMock = { id: 123, firstName: 'John', lastName: 'Doe', age: 50 };
const dataContextMock = { id: 123, firstName: 'John', lastName: '·\u034f ⮞ Doe', age: 50 };
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
plugin.dispose();
plugin.init({ commandItems: [] });
Expand Down
15 changes: 7 additions & 8 deletions packages/common/src/extensions/slickContextMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,21 +381,21 @@ export class SlickContextMenu extends MenuFromCellBaseClass<ContextMenu> {
try {
if (args && args.grid && args.command) {
// get the value, if "exportWithFormatter" is set then we'll use the formatter output
const gridOptions = this.sharedService && this.sharedService.gridOptions || {};
const cell = args && args.cell || 0;
const row = args && args.row || 0;
const gridOptions = this.sharedService?.gridOptions ?? {};
const cell = args?.cell ?? 0;
const row = args?.row ?? 0;
const columnDef = args?.column;
const dataContext = args?.dataContext;
const grid = this.sharedService && this.sharedService.slickGrid;
const exportOptions = gridOptions && (gridOptions.excelExportOptions || { ...gridOptions.exportOptions, ...gridOptions.textExportOptions });
const grid = this.sharedService?.slickGrid;
const exportOptions = gridOptions && ((gridOptions.excelExportOptions || { ...gridOptions.exportOptions, ...gridOptions.textExportOptions }));
let textToCopy = exportWithFormatterWhenDefined(row, cell, columnDef, dataContext, grid, exportOptions);

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

// remove any unwanted Tree Data/Grouping symbols from the beginning of the string before copying (e.g.: "⮟ Task 21" or "· Task 2")
const finalTextToCopy = textToCopy.replace(/^([·|⮞|⮟]\s*)|([·|⮞|⮟])\s*/g, '');
const finalTextToCopy = textToCopy.replace(/^([\u00b7|\u034f|·|⮞|⮟]\s*)|([·|⮞|⮟])\s*/gi, '').replace(/[\u00b7|\u034f]/gi, '').trim();

// 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 @@ -405,8 +405,7 @@ export class SlickContextMenu extends MenuFromCellBaseClass<ContextMenu> {
tmpElem.value = finalTextToCopy;
document.body.appendChild(tmpElem);
tmpElem.select();
const success = document.execCommand('copy', false, textToCopy);
if (success) {
if (document.execCommand('copy', false, finalTextToCopy)) {
tmpElem.remove();
}
}
Expand Down
4 changes: 3 additions & 1 deletion packages/common/src/formatters/formatterUtilities.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FieldType } from '../enums/fieldType.enum';
import { Column, ExcelExportOption, Formatter, GridOption, SlickGrid, TextExportOption } from '../interfaces/index';
import { sanitizeHtmlToText } from '../services/domUtilities';
import { mapMomentDateFormatWithFieldType } from '../services/utilities';
import * as moment_ from 'moment-mini';
import { multipleFormatter } from './multipleFormatter';
Expand Down Expand Up @@ -113,7 +114,8 @@ export function exportWithFormatterWhenDefined<T = any>(row: number, col: number
formatter = columnDef.formatter;
}

return parseFormatterWhenExist(formatter, row, col, columnDef, dataContext, grid);
const output = parseFormatterWhenExist(formatter, row, col, columnDef, dataContext, grid);
return exportOptions?.sanitizeDataExport ? sanitizeHtmlToText(output) : output;
}

/**
Expand Down
Binary file not shown.

0 comments on commit f94ca83

Please sign in to comment.