Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Lens][Datatable] Fix share export and inspect data #193780

Merged
merged 25 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
caf0d04
chore: strengthen context types with default adapter type
nickofthyme Sep 19, 2024
64aaadc
fix: table inspect flyout export data
nickofthyme Sep 19, 2024
3d1d55a
feat: support transposed columns in table
nickofthyme Sep 20, 2024
5158787
improve inspect and export flow
nickofthyme Sep 23, 2024
de71e9f
fix: filter out ghost columns in inspect created from formula metrics
nickofthyme Sep 23, 2024
5bde6b9
Merge branch 'main' into fix-table-exports
nickofthyme Sep 29, 2024
351812f
Merge branch 'main' into fix-table-exports
nickofthyme Oct 13, 2024
1317666
Move transpose helpers into shared kbn package
nickofthyme Oct 13, 2024
dfc2221
cleanup columns sorting logic
nickofthyme Oct 13, 2024
fd5974d
Merge branch 'main' into fix-table-exports
nickofthyme Oct 14, 2024
44baf77
cleanup csv sorting logic, now we directly pass all tables to be expo…
nickofthyme Oct 14, 2024
11f8cb2
[CI] Auto-commit changed files from 'node scripts/lint_packages --fix'
kibanamachine Oct 14, 2024
15eaf73
[CI] Auto-commit changed files from 'node scripts/notice'
kibanamachine Oct 14, 2024
d8abdf6
fix quick check issues
nickofthyme Oct 20, 2024
609dc1c
update jest tests with new changes
nickofthyme Oct 20, 2024
50ffcdf
Merge branch 'main' into fix-table-exports
nickofthyme Oct 20, 2024
1e9f9b5
[CI] Auto-commit changed files from 'node scripts/build_plugin_list_d…
kibanamachine Oct 20, 2024
25a6eb0
fix jest type errors
nickofthyme Oct 21, 2024
5c3ccd8
fix activeData error caused by conditional `onActiveDataChange` trans…
nickofthyme Oct 22, 2024
313c934
Merge remote-tracking branch 'origin/fix-table-exports' into fix-tabl…
nickofthyme Oct 22, 2024
b454f2a
Merge branch 'main' into fix-table-exports
nickofthyme Oct 22, 2024
ac0ff49
fix jest tests and fn tests
nickofthyme Oct 22, 2024
0d580bd
Merge branch 'main' into fix-table-exports
nickofthyme Oct 22, 2024
a37edcb
Merge branch 'main' into fix-table-exports
nickofthyme Oct 24, 2024
4592f10
fix type error in unit test
nickofthyme Oct 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { $Values } from '@kbn/utility-types';
import type { PaletteOutput, CustomPaletteParams } from '@kbn/coloring';
import {
Datatable,
DefaultInspectorAdapters,
ExecutionContext,
ExpressionFunctionDefinition,
ExpressionValueRender,
} from '@kbn/expressions-plugin/common';
Expand Down Expand Up @@ -86,7 +88,8 @@ export type GaugeExpressionFunctionDefinition = ExpressionFunctionDefinition<
typeof EXPRESSION_GAUGE_NAME,
GaugeInput,
GaugeArguments,
ExpressionValueRender<GaugeExpressionProps>
ExpressionValueRender<GaugeExpressionProps>,
ExecutionContext<DefaultInspectorAdapters>
>;

export interface Accessors {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { Position } from '@elastic/charts';
import type { PaletteOutput } from '@kbn/coloring';
import {
Datatable,
DefaultInspectorAdapters,
ExecutionContext,
ExpressionFunctionDefinition,
ExpressionValueRender,
} from '@kbn/expressions-plugin/common';
Expand Down Expand Up @@ -114,7 +116,8 @@ export type HeatmapExpressionFunctionDefinition = ExpressionFunctionDefinition<
typeof EXPRESSION_HEATMAP_NAME,
HeatmapInput,
HeatmapArguments,
ExpressionValueRender<HeatmapExpressionProps>
ExpressionValueRender<HeatmapExpressionProps>,
ExecutionContext<DefaultInspectorAdapters>
>;

export type HeatmapLegendExpressionFunctionDefinition = ExpressionFunctionDefinition<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import type { PaletteOutput } from '@kbn/coloring';
import {
Datatable,
DefaultInspectorAdapters,
ExecutionContext,
ExpressionFunctionDefinition,
ExpressionValueRender,
Style,
Expand Down Expand Up @@ -47,5 +49,6 @@ export type MetricVisExpressionFunctionDefinition = ExpressionFunctionDefinition
typeof EXPRESSION_METRIC_NAME,
MetricInput,
MetricArguments,
ExpressionValueRender<MetricVisRenderConfig>
ExpressionValueRender<MetricVisRenderConfig>,
ExecutionContext<DefaultInspectorAdapters>
>;
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { LayoutDirection, MetricStyle, MetricWTrend } from '@elastic/charts';
import { $Values } from '@kbn/utility-types';
import {
Datatable,
DefaultInspectorAdapters,
ExecutionContext,
ExpressionFunctionDefinition,
ExpressionValueRender,
} from '@kbn/expressions-plugin/common';
Expand Down Expand Up @@ -64,7 +66,8 @@ export type MetricVisExpressionFunctionDefinition = ExpressionFunctionDefinition
typeof EXPRESSION_METRIC_NAME,
MetricInput,
MetricArguments,
ExpressionValueRender<MetricVisRenderConfig>
ExpressionValueRender<MetricVisRenderConfig>,
ExecutionContext<DefaultInspectorAdapters>
>;

export interface TrendlineArguments {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
Datatable,
ExpressionValueRender,
ExpressionValueBoxed,
DefaultInspectorAdapters,
ExecutionContext,
} from '@kbn/expressions-plugin/common';
import {
PARTITION_LABELS_VALUE,
Expand Down Expand Up @@ -66,28 +68,32 @@ export type PieVisExpressionFunctionDefinition = ExpressionFunctionDefinition<
typeof PIE_VIS_EXPRESSION_NAME,
Datatable,
PieVisConfig,
ExpressionValueRender<PartitionChartProps>
ExpressionValueRender<PartitionChartProps>,
ExecutionContext<DefaultInspectorAdapters>
>;

export type TreemapVisExpressionFunctionDefinition = ExpressionFunctionDefinition<
typeof TREEMAP_VIS_EXPRESSION_NAME,
Datatable,
TreemapVisConfig,
ExpressionValueRender<PartitionChartProps>
ExpressionValueRender<PartitionChartProps>,
ExecutionContext<DefaultInspectorAdapters>
>;

export type MosaicVisExpressionFunctionDefinition = ExpressionFunctionDefinition<
typeof MOSAIC_VIS_EXPRESSION_NAME,
Datatable,
MosaicVisConfig,
ExpressionValueRender<PartitionChartProps>
ExpressionValueRender<PartitionChartProps>,
ExecutionContext<DefaultInspectorAdapters>
>;

export type WaffleVisExpressionFunctionDefinition = ExpressionFunctionDefinition<
typeof WAFFLE_VIS_EXPRESSION_NAME,
Datatable,
WaffleVisConfig,
ExpressionValueRender<PartitionChartProps>
ExpressionValueRender<PartitionChartProps>,
ExecutionContext<DefaultInspectorAdapters>
>;

export enum ChartTypes {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import type { PaletteOutput } from '@kbn/coloring';
import type {
Datatable,
DatatableColumnMeta,
DefaultInspectorAdapters,
ExecutionContext,
ExpressionFunctionDefinition,
} from '@kbn/expressions-plugin/common';
import {
Expand Down Expand Up @@ -449,13 +451,15 @@ export type XyVisFn = ExpressionFunctionDefinition<
typeof XY_VIS,
Datatable,
XYArgs,
Promise<XYRender>
Promise<XYRender>,
ExecutionContext<DefaultInspectorAdapters>
>;
export type LayeredXyVisFn = ExpressionFunctionDefinition<
typeof LAYERED_XY_VIS,
Datatable,
LayeredXYArgs,
Promise<XYRender>
Promise<XYRender>,
ExecutionContext<DefaultInspectorAdapters>
>;

export type ExtendedDataLayerFn = ExpressionFunctionDefinition<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
*/

import { QueryPointEventAnnotationOutput } from '@kbn/event-annotation-plugin/common';
import { Datatable, ExecutionContext } from '@kbn/expressions-plugin/common';
import {
Datatable,
DefaultInspectorAdapters,
ExecutionContext,
} from '@kbn/expressions-plugin/common';
import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common';
import { Dimension, prepareLogTable } from '@kbn/visualizations-plugin/common/utils';
import { LayerTypes, REFERENCE_LINE } from '../constants';
Expand All @@ -23,7 +27,7 @@ import {

export const logDatatables = (
layers: CommonXYLayerConfig[],
handlers: ExecutionContext,
handlers: ExecutionContext<DefaultInspectorAdapters>,
splitColumnAccessor?: string | ExpressionValueVisDimension,
splitRowAccessor?: string | ExpressionValueVisDimension,
annotations?: ExpressionAnnotationResult
Expand Down Expand Up @@ -88,7 +92,7 @@ const getLogAnnotationTable = (data: Datatable, layer: AnnotationLayerConfigResu
export const logDatatable = (
data: Datatable,
layers: CommonXYLayerConfig[],
handlers: ExecutionContext,
handlers: ExecutionContext<DefaultInspectorAdapters>,
splitColumnAccessor?: string | ExpressionValueVisDimension,
splitRowAccessor?: string | ExpressionValueVisDimension
) => {
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/common/exports/export_csv.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ describe('CSV exporter', () => {
expect(
datatableToCSV(datatable, {
...getDefaultOptions(),
columnsSorting: ['col2', 'col1'],
sortedColumns: ['col2', 'col1'],
})
).toMatch('columnTwo,columnOne\r\n"Formatted_5","Formatted_value"\r\n');
});
Expand Down
73 changes: 58 additions & 15 deletions src/plugins/data/common/exports/export_csv.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import { Datatable } from '@kbn/expressions-plugin/common';
import { FormatFactory } from '@kbn/field-formats-plugin/common';
import { EuiDataGridColumnSortingConfig } from '@elastic/eui';
import { createEscapeValue } from './escape_value';

export const LINE_FEED_CHARACTER = '\r\n';
Expand All @@ -22,34 +23,54 @@ interface CSVOptions {
escapeFormulaValues: boolean;
formatFactory: FormatFactory;
raw?: boolean;
columnsSorting?: string[];
sortedColumns?: string[];
columnSorting?: EuiDataGridColumnSortingConfig[];
nickofthyme marked this conversation as resolved.
Show resolved Hide resolved
}

export function datatableToCSV(
{ columns, rows }: Datatable,
{ csvSeparator, quoteValues, formatFactory, raw, escapeFormulaValues, columnsSorting }: CSVOptions
{
csvSeparator,
quoteValues,
formatFactory,
raw,
escapeFormulaValues,
sortedColumns,
columnSorting,
}: CSVOptions
) {
const escapeValues = createEscapeValue({
separator: csvSeparator,
quoteValues,
escapeFormulaValues,
});

const sortedIds = columnsSorting || columns.map((col) => col.id);
const sortedIds = sortedColumns
? columns
.map((c) => {
// need to find original id for transposed column
const sortIndex = sortedColumns.findIndex((id) => c.id.endsWith(id));
return {
id: c.id,
sortIndex,
isTransposed: (sortedColumns[sortIndex] ?? '') !== c.id,
};
})
nickofthyme marked this conversation as resolved.
Show resolved Hide resolved
.filter(({ sortIndex }) => sortIndex >= 0)
// keep original zipped order between multiple transposed columns
.sort((a, b) => (a.isTransposed && b.isTransposed ? 0 : a.sortIndex - b.sortIndex))
nickofthyme marked this conversation as resolved.
Show resolved Hide resolved
.map(({ id }) => id)
: columns.map(({ id }) => id);

// Build an index lookup table
const columnIndexLookup = sortedIds.reduce((memo, id, index) => {
memo[id] = index;
return memo;
}, {} as Record<string, number>);
const columnIndexLookup = new Map(sortedIds.map((id, i) => [id, i]));

// Build the header row by its names
const header: string[] = [];
const sortedColumnIds: string[] = [];
const formatters: Record<string, ReturnType<FormatFactory>> = {};

for (const column of columns) {
const columnIndex = columnIndexLookup[column.id];
const columnIndex = columnIndexLookup.get(column.id) ?? -1;
if (columnIndex < 0) continue; // hidden or not found

header[columnIndex] = escapeValues(column.name);
sortedColumnIds[columnIndex] = column.id;
Expand All @@ -61,14 +82,36 @@ export function datatableToCSV(
}

// Convert the array of row objects to an array of row arrays
const csvRows = rows.map((row) => {
return sortedColumnIds.map((id) =>
escapeValues(raw ? row[id] : formatters[id].convert(row[id]))
);
});
const csvRows = rows
.map((row) => {
return sortedColumnIds.map((id) =>
escapeValues(raw ? row[id] : formatters[id].convert(row[id]))
);
})
.sort(rowSortPredicate(sortedColumnIds, columnSorting));

return (
[header, ...csvRows].map((row) => row.join(csvSeparator)).join(LINE_FEED_CHARACTER) +
LINE_FEED_CHARACTER
); // Add \r\n after last line
}

function rowSortPredicate(
sortedColumnIds: string[],
columnSorting?: EuiDataGridColumnSortingConfig[]
) {
if (!columnSorting) return () => 0;

const columnIdMap = new Map(columnSorting.map(({ id }) => [id, sortedColumnIds.indexOf(id)]));
nickofthyme marked this conversation as resolved.
Show resolved Hide resolved
return (rowA: string[], rowB: string[]) => {
nickofthyme marked this conversation as resolved.
Show resolved Hide resolved
return columnSorting.reduce((acc, { id, direction }) => {
const i = columnIdMap.get(id) ?? -1;
if (i < 0) return acc;

const a = rowA[i];
const b = rowB[i];
const emptyValueSort = a === '' ? 1 : b === '' ? -1 : 0; // always put empty values at bottom
return acc || emptyValueSort || a.localeCompare(b) * (direction === 'asc' ? 1 : -1);
}, 0);
};
}
nickofthyme marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import React, { Component } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import PropTypes from 'prop-types';
import {
EuiButtonEmpty,
EuiContextMenuPanel,
Expand All @@ -27,16 +26,10 @@ interface TableSelectorState {
interface TableSelectorProps {
tables: Datatable[];
selectedTable: Datatable;
onTableChanged: Function;
onTableChanged: (table: Datatable) => void;
}

export class TableSelector extends Component<TableSelectorProps, TableSelectorState> {
static propTypes = {
tables: PropTypes.array.isRequired,
selectedTable: PropTypes.object.isRequired,
onTableChanged: PropTypes.func,
};

state = {
isPopoverOpen: false,
};
Expand Down Expand Up @@ -85,35 +78,37 @@ export class TableSelector extends Component<TableSelectorProps, TableSelectorSt
/>
</strong>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<EuiPopover
id="inspectorTableChooser"
button={
<EuiButtonEmpty
iconType="arrowDown"
iconSide="right"
size="s"
onClick={this.togglePopover}
data-test-subj="inspectorTableChooser"
>
<FormattedMessage
id="data.inspector.table.inspectorTableChooserButton"
defaultMessage="Table {index}"
values={{ index: currentIndex + 1 }}
/>
</EuiButtonEmpty>
}
isOpen={this.state.isPopoverOpen}
closePopover={this.closePopover}
panelPaddingSize="none"
anchorPosition="downLeft"
repositionOnScroll
>
<EuiContextMenuPanel
items={this.props.tables.map(this.renderTableDropdownItem)}
data-test-subj="inspectorTableChooserMenuPanel"
/>
</EuiPopover>
<EuiFlexItem>
<div>
<EuiPopover
id="inspectorTableChooser"
button={
<EuiButtonEmpty
iconType="arrowDown"
iconSide="right"
size="s"
onClick={this.togglePopover}
data-test-subj="inspectorTableChooser"
>
<FormattedMessage
id="data.inspector.table.inspectorTableChooserButton"
defaultMessage="Table {index}"
values={{ index: currentIndex + 1 }}
/>
</EuiButtonEmpty>
}
isOpen={this.state.isPopoverOpen}
closePopover={this.closePopover}
panelPaddingSize="none"
anchorPosition="downLeft"
repositionOnScroll
>
<EuiContextMenuPanel
items={this.props.tables.map(this.renderTableDropdownItem)}
data-test-subj="inspectorTableChooserMenuPanel"
/>
</EuiPopover>
</div>
nickofthyme marked this conversation as resolved.
Show resolved Hide resolved
</EuiFlexItem>
</EuiFlexGroup>
);
Expand Down
Loading