Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 15 additions & 12 deletions src/plugins/vis_type_table/public/components/table_vis_basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@ import { orderBy } from 'lodash';

import { IInterpreterRenderHandlers } from 'src/plugins/expressions';
import { createTableVisCell } from './table_vis_cell';
import { Table } from '../table_vis_response_handler';
import { TableVisConfig, TableVisUseUiStateProps } from '../types';
import { useFormattedColumnsAndRows, usePagination } from '../utils';
import { TableContext, TableVisConfig, TableVisUseUiStateProps } from '../types';
import { usePagination } from '../utils';
import { TableVisControls } from './table_vis_controls';
import { createGridColumns } from './table_vis_columns';

interface TableVisBasicProps {
fireEvent: IInterpreterRenderHandlers['event'];
table: Table;
table: TableContext;
visConfig: TableVisConfig;
title?: string;
uiStateProps: TableVisUseUiStateProps;
Expand All @@ -35,7 +34,7 @@ export const TableVisBasic = memo(
title,
uiStateProps: { columnsWidth, sort, setColumnsWidth, setSort },
}: TableVisBasicProps) => {
const { columns, rows } = useFormattedColumnsAndRows(table, visConfig);
const { columns, rows, formattedColumns } = table;

// custom sorting is in place until the EuiDataGrid sorting gets rid of flaws -> https://github.com/elastic/eui/issues/4108
const sortedRows = useMemo(
Expand All @@ -47,13 +46,19 @@ export const TableVisBasic = memo(
);

// renderCellValue is a component which renders a cell based on column and row indexes
const renderCellValue = useMemo(() => createTableVisCell(columns, sortedRows), [
columns,
const renderCellValue = useMemo(() => createTableVisCell(sortedRows, formattedColumns), [
formattedColumns,
sortedRows,
]);

// Columns config
const gridColumns = createGridColumns(table, columns, columnsWidth, sortedRows, fireEvent);
const gridColumns = createGridColumns(
columns,
sortedRows,
formattedColumns,
columnsWidth,
fireEvent
);

// Pagination config
const pagination = usePagination(visConfig, rows.length);
Expand Down Expand Up @@ -126,10 +131,9 @@ export const TableVisBasic = memo(
additionalControls: (
<TableVisControls
dataGridAriaLabel={dataGridAriaLabel}
cols={columns}
columns={columns}
// csv exports sorted table
rows={sortedRows}
table={table}
filename={visConfig.title}
/>
),
Expand All @@ -138,8 +142,7 @@ export const TableVisBasic = memo(
renderCellValue={renderCellValue}
renderFooterCellValue={
visConfig.showTotal
? // @ts-expect-error
({ colIndex }) => columns[colIndex].formattedTotal || null
? ({ columnId }) => formattedColumns[columnId].formattedTotal || null
: undefined
}
pagination={pagination}
Expand Down
10 changes: 4 additions & 6 deletions src/plugins/vis_type_table/public/components/table_vis_cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,15 @@
import React from 'react';
import { EuiDataGridCellValueElementProps } from '@elastic/eui';

import { Table } from '../table_vis_response_handler';
import { FormattedColumn } from '../types';
import { DatatableRow } from 'src/plugins/expressions';
import { FormattedColumns } from '../types';

export const createTableVisCell = (formattedColumns: FormattedColumn[], rows: Table['rows']) => ({
// @ts-expect-error
colIndex,
export const createTableVisCell = (rows: DatatableRow[], formattedColumns: FormattedColumns) => ({
rowIndex,
columnId,
}: EuiDataGridCellValueElementProps) => {
const rowValue = rows[rowIndex][columnId];
const column = formattedColumns[colIndex];
const column = formattedColumns[columnId];
const content = column.formatter.convert(rowValue, 'html');

const cellContent = (
Expand Down
35 changes: 13 additions & 22 deletions src/plugins/vis_type_table/public/components/table_vis_columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ import React from 'react';
import { EuiDataGridColumnCellActionProps, EuiDataGridColumn } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import { IInterpreterRenderHandlers } from 'src/plugins/expressions';
import { Table } from '../table_vis_response_handler';
import { FormattedColumn, TableVisUiState } from '../types';
import { DatatableColumn, DatatableRow, IInterpreterRenderHandlers } from 'src/plugins/expressions';
import { FormattedColumns, TableVisUiState } from '../types';

interface FilterCellData {
/**
Expand All @@ -27,33 +26,24 @@ interface FilterCellData {
}

export const createGridColumns = (
table: Table,
columns: FormattedColumn[],
columns: DatatableColumn[],
rows: DatatableRow[],
formattedColumns: FormattedColumns,
columnsWidth: TableVisUiState['colWidth'],
rows: Table['rows'],
fireEvent: IInterpreterRenderHandlers['event']
) => {
const onFilterClick = (data: FilterCellData, negate: boolean) => {
/**
* Visible column index and the actual one from the source table could be different.
* e.x. a column could be filtered out if it's not a dimension -
* see formattedColumns in use_formatted_columns.ts file,
* or an extra percantage column could be added, which doesn't exist in the raw table
*/
const rawTableActualColumnIndex = table.columns.findIndex(
(c) => c.id === columns[data.column].id
);
fireEvent({
name: 'filterBucket',
data: {
data: [
{
table: {
...table,
columns,
rows,
},
...data,
column: rawTableActualColumnIndex,
column: data.column,
},
],
negate,
Expand All @@ -63,12 +53,13 @@ export const createGridColumns = (

return columns.map(
(col, colIndex): EuiDataGridColumn => {
const cellActions = col.filterable
const formattedColumn = formattedColumns[col.id];
const cellActions = formattedColumn.filterable
? [
({ rowIndex, columnId, Component, closePopover }: EuiDataGridColumnCellActionProps) => {
const rowValue = rows[rowIndex][columnId];
const contentsIsDefined = rowValue !== null && rowValue !== undefined;
const cellContent = col.formatter.convert(rowValue);
const cellContent = formattedColumn.formatter.convert(rowValue);

const filterForText = i18n.translate(
'visTypeTable.tableCellFilter.filterForValueText',
Expand Down Expand Up @@ -105,7 +96,7 @@ export const createGridColumns = (
({ rowIndex, columnId, Component, closePopover }: EuiDataGridColumnCellActionProps) => {
const rowValue = rows[rowIndex][columnId];
const contentsIsDefined = rowValue !== null && rowValue !== undefined;
const cellContent = col.formatter.convert(rowValue);
const cellContent = formattedColumn.formatter.convert(rowValue);

const filterOutText = i18n.translate(
'visTypeTable.tableCellFilter.filterOutValueText',
Expand Down Expand Up @@ -144,8 +135,8 @@ export const createGridColumns = (
const initialWidth = columnsWidth.find((c) => c.colIndex === colIndex);
const column: EuiDataGridColumn = {
id: col.id,
display: col.title,
displayAsText: col.title,
display: col.name,
displayAsText: col.name,
actions: {
showHide: false,
showMoveLeft: false,
Expand Down
148 changes: 85 additions & 63 deletions src/plugins/vis_type_table/public/components/table_vis_controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,81 +11,103 @@ import { EuiButtonEmpty, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } f
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';

import { DatatableRow } from 'src/plugins/expressions';
import { DatatableColumn, DatatableRow } from 'src/plugins/expressions';
import { CoreStart } from 'kibana/public';
import { useKibana } from '../../../kibana_react/public';
import { FormattedColumn } from '../types';
import { Table } from '../table_vis_response_handler';
import { exportAsCsv } from '../utils';
import { exporters } from '../../../data/public';
import {
CSV_SEPARATOR_SETTING,
CSV_QUOTE_VALUES_SETTING,
downloadFileAs,
} from '../../../share/public';
import { getFormatService } from '../services';

interface TableVisControlsProps {
dataGridAriaLabel: string;
filename?: string;
cols: FormattedColumn[];
columns: DatatableColumn[];
rows: DatatableRow[];
table: Table;
}

export const TableVisControls = memo(({ dataGridAriaLabel, ...props }: TableVisControlsProps) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const togglePopover = useCallback(() => setIsPopoverOpen((state) => !state), []);
const closePopover = useCallback(() => setIsPopoverOpen(false), []);
export const TableVisControls = memo(
({ dataGridAriaLabel, filename, columns, rows }: TableVisControlsProps) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const togglePopover = useCallback(() => setIsPopoverOpen((state) => !state), []);
const closePopover = useCallback(() => setIsPopoverOpen(false), []);

const {
services: { uiSettings },
} = useKibana<CoreStart>();
const {
services: { uiSettings },
} = useKibana<CoreStart>();

const onClickExport = useCallback(
(formatted: boolean) =>
exportAsCsv(formatted, {
...props,
uiSettings,
}),
[props, uiSettings]
);
const onClickExport = useCallback(
(formatted: boolean) => {
const csvSeparator = uiSettings.get(CSV_SEPARATOR_SETTING);
const quoteValues = uiSettings.get(CSV_QUOTE_VALUES_SETTING);

const exportBtnAriaLabel = i18n.translate('visTypeTable.vis.controls.exportButtonAriaLabel', {
defaultMessage: 'Export {dataGridAriaLabel} as CSV',
values: {
dataGridAriaLabel,
},
});
const content = exporters.datatableToCSV(
{
type: 'datatable',
columns,
rows,
},
{
csvSeparator,
quoteValues,
formatFactory: getFormatService().deserialize,
raw: !formatted,
}
);
downloadFileAs(`${filename || 'unsaved'}.csv`, { content, type: exporters.CSV_MIME_TYPE });
},
[columns, rows, filename, uiSettings]
);

const button = (
<EuiButtonEmpty
aria-label={exportBtnAriaLabel}
size="xs"
iconType="exportAction"
color="text"
className="euiDataGrid__controlBtn"
onClick={togglePopover}
>
<FormattedMessage id="visTypeTable.vis.controls.exportButtonLabel" defaultMessage="Export" />
</EuiButtonEmpty>
);
const exportBtnAriaLabel = i18n.translate('visTypeTable.vis.controls.exportButtonAriaLabel', {
defaultMessage: 'Export {dataGridAriaLabel} as CSV',
values: {
dataGridAriaLabel,
},
});

const items = [
<EuiContextMenuItem key="rawCsv" onClick={() => onClickExport(false)}>
<FormattedMessage id="visTypeTable.vis.controls.rawCSVButtonLabel" defaultMessage="Raw" />
</EuiContextMenuItem>,
<EuiContextMenuItem key="csv" onClick={() => onClickExport(true)}>
<FormattedMessage
id="visTypeTable.vis.controls.formattedCSVButtonLabel"
defaultMessage="Formatted"
/>
</EuiContextMenuItem>,
];
const button = (
<EuiButtonEmpty
aria-label={exportBtnAriaLabel}
size="xs"
iconType="exportAction"
color="text"
className="euiDataGrid__controlBtn"
onClick={togglePopover}
>
<FormattedMessage
id="visTypeTable.vis.controls.exportButtonLabel"
defaultMessage="Export"
/>
</EuiButtonEmpty>
);

return (
<EuiPopover
id="dataTableExportData"
button={button}
isOpen={isPopoverOpen}
closePopover={closePopover}
panelPaddingSize="none"
repositionOnScroll
>
<EuiContextMenuPanel className="eui-textNoWrap" items={items} />
</EuiPopover>
);
});
const items = [
<EuiContextMenuItem key="rawCsv" onClick={() => onClickExport(false)}>
<FormattedMessage id="visTypeTable.vis.controls.rawCSVButtonLabel" defaultMessage="Raw" />
</EuiContextMenuItem>,
<EuiContextMenuItem key="csv" onClick={() => onClickExport(true)}>
<FormattedMessage
id="visTypeTable.vis.controls.formattedCSVButtonLabel"
defaultMessage="Formatted"
/>
</EuiContextMenuItem>,
];

return (
<EuiPopover
id="dataTableExportData"
button={button}
isOpen={isPopoverOpen}
closePopover={closePopover}
panelPaddingSize="none"
repositionOnScroll
>
<EuiContextMenuPanel className="eui-textNoWrap" items={items} />
</EuiPopover>
);
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
import React, { memo } from 'react';

import { IInterpreterRenderHandlers } from 'src/plugins/expressions';
import { TableGroup } from '../table_vis_response_handler';
import { TableVisConfig, TableVisUseUiStateProps } from '../types';
import { TableGroup, TableVisConfig, TableVisUseUiStateProps } from '../types';
import { TableVisBasic } from './table_vis_basic';

interface TableVisSplitProps {
Expand All @@ -24,11 +23,11 @@ export const TableVisSplit = memo(
({ fireEvent, tables, visConfig, uiStateProps }: TableVisSplitProps) => {
return (
<>
{tables.map(({ tables: dataTable, key, title }) => (
<div key={key} className="tbvChart__split">
{tables.map(({ table, title }) => (
<div key={title} className="tbvChart__split">
<TableVisBasic
fireEvent={fireEvent}
table={dataTable[0]}
table={table}
visConfig={visConfig}
title={title}
uiStateProps={uiStateProps}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@ import { CoreStart } from 'kibana/public';
import { IInterpreterRenderHandlers } from 'src/plugins/expressions';
import type { PersistedState } from 'src/plugins/visualizations/public';
import { KibanaContextProvider } from '../../../kibana_react/public';
import { TableVisConfig } from '../types';
import { TableContext } from '../table_vis_response_handler';
import { TableVisConfig, TableVisData } from '../types';
import { TableVisBasic } from './table_vis_basic';
import { TableVisSplit } from './table_vis_split';
import { useUiState } from '../utils';

interface TableVisualizationComponentProps {
core: CoreStart;
handlers: IInterpreterRenderHandlers;
visData: TableContext;
visData: TableVisData;
visConfig: TableVisConfig;
}

Expand Down
Loading