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 66f1264a63e17..079a26c265d27 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 5c565a97df8dc..59cd130277f90 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 77e7c5b2a5e17..fd7122fccbd95 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 2419f15b8a429..a0fe48fcc574b 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 a6881e222dd62..dd81ad621f182 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 1e98ca2b3d9fc..f67abe8d53dc2 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 bcb9bbf115357..c700fa748f335 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 ed483e8a15b76..6f51066d6e78c 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 e24198e340175..ba1d4aa2e7032 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 1462baa539c28..8d63736098da3 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": "复制列",