diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid_cell_actions.test.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid_cell_actions.test.tsx index 66f1264a63e1793..079a26c265d276e 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid_cell_actions.test.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid_cell_actions.test.tsx @@ -38,7 +38,21 @@ import { DataViewField } from '@kbn/data-views-plugin/public'; describe('Discover cell actions ', function () { it('should not show cell actions for unfilterable fields', async () => { - expect(buildCellActions({ name: 'foo', filterable: false } as DataViewField)).toBeUndefined(); + expect(buildCellActions({ name: 'foo', filterable: false } as DataViewField)).toEqual([ + CopyBtn, + ]); + }); + + it('should show filter actions for filterable fields', async () => { + expect(buildCellActions({ name: 'foo', filterable: true } as DataViewField, jest.fn())).toEqual( + [FilterInBtn, FilterOutBtn, CopyBtn] + ); + }); + + it('should show Copy action for _source field', async () => { + expect( + buildCellActions({ name: '_source', type: '_source', filterable: false } as DataViewField) + ).toEqual([CopyBtn]); }); it('triggers filter function when FilterInBtn is clicked', async () => { diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid_cell_actions.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid_cell_actions.tsx index 5c565a97df8dc14..59cd130277f9024 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid_cell_actions.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid_cell_actions.tsx @@ -110,19 +110,13 @@ export const CopyBtn = ({ Component, rowIndex, columnId }: EuiDataGridColumnCell title={buttonTitle} data-test-subj="copyClipboardButton" > - {i18n.translate('discover.grid.copyClipboardButton', { - defaultMessage: 'Copy to clipboard', + {i18n.translate('discover.grid.copyCellValueButton', { + defaultMessage: 'Copy value', })} ); }; export function buildCellActions(field: DataViewField, onFilter?: DocViewFilterFn) { - if (field?.type === '_source') { - return [CopyBtn]; - } else if (!onFilter || !field.filterable) { - return undefined; - } - - return [FilterInBtn, FilterOutBtn]; + return [...(onFilter && field.filterable ? [FilterInBtn, FilterOutBtn] : []), CopyBtn]; } diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid_columns.test.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid_columns.test.tsx index 77e7c5b2a5e1714..fd7122fccbd9565 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid_columns.test.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid_columns.test.tsx @@ -75,6 +75,7 @@ describe('Discover grid columns', function () { "cellActions": Array [ [Function], [Function], + [Function], ], "displayAsText": "extension", "id": "extension", @@ -120,7 +121,9 @@ describe('Discover grid columns', function () { "showMoveLeft": true, "showMoveRight": true, }, - "cellActions": undefined, + "cellActions": Array [ + [Function], + ], "displayAsText": "message", "id": "message", "isSortable": false, @@ -188,6 +191,7 @@ describe('Discover grid columns', function () { "cellActions": Array [ [Function], [Function], + [Function], ], "displayAsText": "extension", "id": "extension", @@ -230,7 +234,9 @@ describe('Discover grid columns', function () { "showMoveLeft": false, "showMoveRight": false, }, - "cellActions": undefined, + "cellActions": Array [ + [Function], + ], "displayAsText": "message", "id": "message", "isSortable": false, @@ -298,6 +304,7 @@ describe('Discover grid columns', function () { "cellActions": Array [ [Function], [Function], + [Function], ], "display":
- + {closeButton} diff --git a/src/plugins/discover/public/types.ts b/src/plugins/discover/public/types.ts index 2419f15b8a429c5..a0fe48fcc574b3f 100644 --- a/src/plugins/discover/public/types.ts +++ b/src/plugins/discover/public/types.ts @@ -12,7 +12,7 @@ import { type DatatableColumn } from '@kbn/expressions-plugin/common'; export type ValueToStringConverter = ( rowIndex: number, columnId: string, - options?: { disableMultiline?: boolean } + options?: { compatibleWithCSV?: boolean } ) => { formattedString: string; withFormula: boolean }; export interface EsHitRecord extends Omit { diff --git a/src/plugins/discover/public/utils/convert_value_to_string.test.tsx b/src/plugins/discover/public/utils/convert_value_to_string.test.tsx index a6881e222dd62f3..dd81ad621f1822d 100644 --- a/src/plugins/discover/public/utils/convert_value_to_string.test.tsx +++ b/src/plugins/discover/public/utils/convert_value_to_string.test.tsx @@ -19,7 +19,7 @@ describe('convertValueToString', () => { columnId: 'keyword_key', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -34,13 +34,28 @@ describe('convertValueToString', () => { columnId: 'text_message', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); expect(result.formattedString).toBe('"Hi there! I am a sample string."'); }); + it('should convert a text value to text (not for CSV)', () => { + const result = convertValueToString({ + rows: discoverGridContextComplexMock.rows, + dataView: discoverGridContextComplexMock.dataView, + fieldFormats: discoverServiceMock.fieldFormats, + columnId: 'text_message', + rowIndex: 0, + options: { + compatibleWithCSV: false, + }, + }); + + expect(result.formattedString).toBe('Hi there! I am a sample string.'); + }); + it('should convert a multiline text value to text', () => { const result = convertValueToString({ rows: discoverGridContextComplexMock.rows, @@ -49,7 +64,7 @@ describe('convertValueToString', () => { columnId: 'text_message', rowIndex: 1, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -65,7 +80,7 @@ describe('convertValueToString', () => { columnId: 'number_price', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -80,7 +95,7 @@ describe('convertValueToString', () => { columnId: 'date', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -95,13 +110,28 @@ describe('convertValueToString', () => { columnId: 'date_nanos', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); expect(result.formattedString).toBe('"2022-01-01T12:10:30.123456789Z"'); }); + it('should convert a date nanos value to text (not for CSV)', () => { + const result = convertValueToString({ + rows: discoverGridContextComplexMock.rows, + dataView: discoverGridContextComplexMock.dataView, + fieldFormats: discoverServiceMock.fieldFormats, + columnId: 'date_nanos', + rowIndex: 0, + options: { + compatibleWithCSV: false, + }, + }); + + expect(result.formattedString).toBe('2022-01-01T12:10:30.123456789Z'); + }); + it('should convert a boolean value to text', () => { const result = convertValueToString({ rows: discoverGridContextComplexMock.rows, @@ -110,7 +140,7 @@ describe('convertValueToString', () => { columnId: 'bool_enabled', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -125,13 +155,28 @@ describe('convertValueToString', () => { columnId: 'binary_blob', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); expect(result.formattedString).toBe('"U29tZSBiaW5hcnkgYmxvYg=="'); }); + it('should convert a binary value to text (not for CSV)', () => { + const result = convertValueToString({ + rows: discoverGridContextComplexMock.rows, + dataView: discoverGridContextComplexMock.dataView, + fieldFormats: discoverServiceMock.fieldFormats, + columnId: 'binary_blob', + rowIndex: 0, + options: { + compatibleWithCSV: false, + }, + }); + + expect(result.formattedString).toBe('U29tZSBiaW5hcnkgYmxvYg=='); + }); + it('should convert an object value to text', () => { const result = convertValueToString({ rows: discoverGridContextComplexMock.rows, @@ -140,7 +185,7 @@ describe('convertValueToString', () => { columnId: 'object_user.first', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -155,7 +200,7 @@ describe('convertValueToString', () => { columnId: 'nested_user', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -172,7 +217,7 @@ describe('convertValueToString', () => { columnId: 'flattened_labels', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -187,7 +232,7 @@ describe('convertValueToString', () => { columnId: 'range_time_frame', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -204,7 +249,7 @@ describe('convertValueToString', () => { columnId: 'rank_features', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -219,7 +264,7 @@ describe('convertValueToString', () => { columnId: 'histogram', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -234,13 +279,28 @@ describe('convertValueToString', () => { columnId: 'ip_addr', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); expect(result.formattedString).toBe('"192.168.1.1"'); }); + it('should convert a IP value to text (not for CSV)', () => { + const result = convertValueToString({ + rows: discoverGridContextComplexMock.rows, + dataView: discoverGridContextComplexMock.dataView, + fieldFormats: discoverServiceMock.fieldFormats, + columnId: 'ip_addr', + rowIndex: 0, + options: { + compatibleWithCSV: false, + }, + }); + + expect(result.formattedString).toBe('192.168.1.1'); + }); + it('should convert a version value to text', () => { const result = convertValueToString({ rows: discoverGridContextComplexMock.rows, @@ -249,13 +309,28 @@ describe('convertValueToString', () => { columnId: 'version', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); expect(result.formattedString).toBe('"1.2.3"'); }); + it('should convert a version value to text (not for CSV)', () => { + const result = convertValueToString({ + rows: discoverGridContextComplexMock.rows, + dataView: discoverGridContextComplexMock.dataView, + fieldFormats: discoverServiceMock.fieldFormats, + columnId: 'version', + rowIndex: 0, + options: { + compatibleWithCSV: false, + }, + }); + + expect(result.formattedString).toBe('1.2.3'); + }); + it('should convert a vector value to text', () => { const result = convertValueToString({ rows: discoverGridContextComplexMock.rows, @@ -264,7 +339,7 @@ describe('convertValueToString', () => { columnId: 'vector', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -279,7 +354,7 @@ describe('convertValueToString', () => { columnId: 'geo_point', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -294,7 +369,7 @@ describe('convertValueToString', () => { columnId: 'geo_point', rowIndex: 1, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -309,7 +384,7 @@ describe('convertValueToString', () => { columnId: 'array_tags', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -324,7 +399,7 @@ describe('convertValueToString', () => { columnId: 'geometry', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -341,7 +416,7 @@ describe('convertValueToString', () => { columnId: 'runtime_number', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -356,13 +431,28 @@ describe('convertValueToString', () => { columnId: 'scripted_string', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); expect(result.formattedString).toBe('"hi there"'); }); + it('should convert a scripted value to text (not for CSV)', () => { + const result = convertValueToString({ + rows: discoverGridContextComplexMock.rows, + dataView: discoverGridContextComplexMock.dataView, + fieldFormats: discoverServiceMock.fieldFormats, + columnId: 'scripted_string', + rowIndex: 0, + options: { + compatibleWithCSV: false, + }, + }); + + expect(result.formattedString).toBe('hi there'); + }); + it('should return an empty string and not fail', () => { const result = convertValueToString({ rows: discoverGridContextComplexMock.rows, @@ -371,7 +461,7 @@ describe('convertValueToString', () => { columnId: 'unknown', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -386,7 +476,7 @@ describe('convertValueToString', () => { columnId: 'unknown', rowIndex: -1, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -401,7 +491,7 @@ describe('convertValueToString', () => { columnId: '_source', rowIndex: 0, options: { - disableMultiline: false, + compatibleWithCSV: false, }, }); @@ -424,7 +514,7 @@ describe('convertValueToString', () => { columnId: '_source', rowIndex: 0, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -441,7 +531,7 @@ describe('convertValueToString', () => { columnId: 'array_tags', rowIndex: 1, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -455,7 +545,7 @@ describe('convertValueToString', () => { columnId: 'scripted_string', rowIndex: 1, options: { - disableMultiline: true, + compatibleWithCSV: true, }, }); @@ -463,6 +553,22 @@ describe('convertValueToString', () => { expect(result2.withFormula).toBe(true); }); + it('should not escape formulas when not for CSV', () => { + const result = convertValueToString({ + rows: discoverGridContextComplexMock.rows, + dataView: discoverGridContextComplexMock.dataView, + fieldFormats: discoverServiceMock.fieldFormats, + columnId: 'array_tags', + rowIndex: 1, + options: { + compatibleWithCSV: false, + }, + }); + + expect(result.formattedString).toBe('=1+2\'" ;,=1+2'); + expect(result.withFormula).toBe(true); + }); + it('should return a formatted name', () => { const result = convertNameToString('test'); diff --git a/src/plugins/discover/public/utils/convert_value_to_string.ts b/src/plugins/discover/public/utils/convert_value_to_string.ts index 1e98ca2b3d9fcc0..f67abe8d53dc245 100644 --- a/src/plugins/discover/public/utils/convert_value_to_string.ts +++ b/src/plugins/discover/public/utils/convert_value_to_string.ts @@ -31,7 +31,7 @@ export const convertValueToString = ({ dataView: DataView; fieldFormats: FieldFormatsStart; options?: { - disableMultiline?: boolean; + compatibleWithCSV?: boolean; // values as one-liner + escaping formulas + adding wrapping quotes }; }): ConvertedResult => { if (!rows[rowIndex]) { @@ -44,7 +44,8 @@ export const convertValueToString = ({ const value = rowFlattened?.[columnId]; const field = dataView.fields.getByName(columnId); const valuesArray = Array.isArray(value) ? value : [value]; - const disableMultiline = options?.disableMultiline ?? false; + const disableMultiline = options?.compatibleWithCSV ?? false; + const enableEscapingForValue = options?.compatibleWithCSV ?? false; if (field?.type === '_source') { return { @@ -71,7 +72,7 @@ export const convertValueToString = ({ if (typeof formattedValue === 'string') { withFormula = withFormula || cellHasFormulas(formattedValue); - return escapeFormattedValue(formattedValue); + return enableEscapingForValue ? escapeFormattedValue(formattedValue) : formattedValue; } return stringify(formattedValue, disableMultiline) || ''; diff --git a/src/plugins/discover/public/utils/copy_value_to_clipboard.ts b/src/plugins/discover/public/utils/copy_value_to_clipboard.ts index bcb9bbf115357c3..c700fa748f33538 100644 --- a/src/plugins/discover/public/utils/copy_value_to_clipboard.ts +++ b/src/plugins/discover/public/utils/copy_value_to_clipboard.ts @@ -81,7 +81,7 @@ export const copyColumnValuesToClipboard = async ({ let withFormula = nameFormattedResult.withFormula; const valuesFormatted = [...Array(rowsCount)].map((_, rowIndex) => { - const result = valueToStringConverter(rowIndex, columnId, { disableMultiline: true }); + const result = valueToStringConverter(rowIndex, columnId, { compatibleWithCSV: true }); withFormula = withFormula || result.withFormula; return result.formattedString; }); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index ed483e8a15b76c6..6f51066d6e78c6d 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -2272,7 +2272,6 @@ "discover.fieldList.flyoutBackIcon": "Retour", "discover.fieldList.flyoutHeading": "Liste des champs", "discover.grid.closePopover": "Fermer la fenêtre contextuelle", - "discover.grid.copyClipboardButton": "Copier dans le presse-papiers", "discover.grid.copyColumnNameToClipboard.toastTitle": "Copié dans le presse-papiers", "discover.grid.copyColumnNameToClipBoardButton": "Copier le nom", "discover.grid.copyColumnValuesToClipBoardButton": "Copier la colonne", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e24198e3401753b..ba1d4aa2e703253 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2270,7 +2270,6 @@ "discover.fieldList.flyoutBackIcon": "戻る", "discover.fieldList.flyoutHeading": "フィールドリスト", "discover.grid.closePopover": "ポップオーバーを閉じる", - "discover.grid.copyClipboardButton": "クリップボードにコピー", "discover.grid.copyColumnNameToClipboard.toastTitle": "クリップボードにコピーされました", "discover.grid.copyColumnNameToClipBoardButton": "名前をコピー", "discover.grid.copyColumnValuesToClipBoardButton": "列をコピー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 1462baa539c28c0..8d63736098da379 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2274,7 +2274,6 @@ "discover.fieldList.flyoutBackIcon": "返回", "discover.fieldList.flyoutHeading": "字段列表", "discover.grid.closePopover": "关闭弹出框", - "discover.grid.copyClipboardButton": "复制到剪贴板", "discover.grid.copyColumnNameToClipboard.toastTitle": "已复制到剪贴板", "discover.grid.copyColumnNameToClipBoardButton": "复制名称", "discover.grid.copyColumnValuesToClipBoardButton": "复制列",