Skip to content

Commit

Permalink
Implement table using table gird and remove doc_viewer_link
Browse files Browse the repository at this point in the history
Issue Resolve
opensearch-project#4442

Signed-off-by: ananzh <ananzh@amazon.com>
  • Loading branch information
ananzh committed Jul 9, 2023
1 parent 6de5b35 commit 7d70608
Show file tree
Hide file tree
Showing 20 changed files with 860 additions and 557 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export const toolbarVisibility = {
showColumnSelector: {
allowHide: false,
allowReorder: true,
},
showStyleSelector: false,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.dscDiscoverGrid {
height: 100%;
width: 100%;
overflow: hidden;

.euiDataGrid__controls {
border: $euiBorderThin;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import './data_grid_table.scss';
import React, { useState, useMemo, useCallback } from 'react';
import { EuiDataGrid } from '@elastic/eui';
import { IndexPattern } from '../../../opensearch_dashboards_services';
import { fetchTableDataCell } from './data_grid_table_cell_value';
import { buildDataGridColumns, computeVisibleColumns } from './data_grid_table_columns';
import { DocViewExpandButton } from './data_grid_table_docview_expand_button';
import { DataGridFlyout } from './data_grid_table_flyout';
import { DataGridContext } from './data_grid_table_context';
import { toolbarVisibility } from './constants';
import { DocViewFilterFn } from '../../doc_views/doc_views_types';
import { DiscoverServices } from '../../../build_services';
import { OpenSearchSearchHit } from '../../doc_views/doc_views_types';
import { usePagination } from '../utils/use_pagination';

export interface DataGridTableProps {
columns: string[];
indexPattern: IndexPattern;
onAddColumn: (column: string) => void;
onFilter: DocViewFilterFn;
onRemoveColumn: (column: string) => void;
onSort: (sort: string[][]) => void;
rows: OpenSearchSearchHit[];
onSetColumns: (columns: string[]) => void;
sort: Array<[string, string]>;
displayTimeColumn: boolean;
services: DiscoverServices;
}

export const DataGridTable = ({
columns,
indexPattern,
onAddColumn,
onFilter,
onRemoveColumn,
onSetColumns,
onSort,
sort,
rows,
displayTimeColumn,
services,
}: DataGridTableProps) => {
const [docViewExpand, setDocViewExpand] = useState(undefined);
const rowCount = useMemo(() => (rows ? rows.length : 0), [rows]);
const pagination = usePagination(rowCount);

const sortingColumns = useMemo(() => sort.map(([id, direction]) => ({ id, direction })), [sort]);

const onColumnSort = useCallback(
(cols) => {
onSort(cols.map(({ id, direction }: any) => [id, direction]));
},
[onSort]
);

const renderCellValue = useMemo(() => fetchTableDataCell(indexPattern, rows), [
indexPattern,
rows,
]);

const dataGridTableColumns = useMemo(
() => buildDataGridColumns(columns, indexPattern, displayTimeColumn),
[columns, indexPattern, displayTimeColumn]
);

const dataGridTableColumnsVisibility = useMemo(
() => ({
visibleColumns: computeVisibleColumns(columns, indexPattern, displayTimeColumn) as string[],
setVisibleColumns: (newColumns: string[]) => {
onSetColumns(newColumns);
},
}),
[columns, indexPattern, displayTimeColumn, onSetColumns]
);

const sorting = useMemo(() => ({ columns: sortingColumns, onSort: onColumnSort }), [
sortingColumns,
onColumnSort,
]);

const leadingControlColumns = useMemo(() => {
return [
{
id: 'expandCollapseColumn',
headerCellRender: () => null,
rowCellRender: DocViewExpandButton,
width: 40,
},
];
}, []);

return (
<DataGridContext.Provider
value={{
docViewExpand,
onFilter,
setDocViewExpand,
rows: rows || [],
indexPattern,
}}
>
<>
<EuiDataGrid
aria-labelledby="aria-labelledby"
columns={dataGridTableColumns}
columnVisibility={dataGridTableColumnsVisibility}
leadingControlColumns={leadingControlColumns}
data-test-subj="docTable"
pagination={pagination}
renderCellValue={renderCellValue}
rowCount={rowCount}
sorting={sorting}
toolbarVisibility={toolbarVisibility}
/>
{docViewExpand && (
<DataGridFlyout
indexPattern={indexPattern}
hit={docViewExpand}
columns={columns}
onRemoveColumn={onRemoveColumn}
onAddColumn={onAddColumn}
onFilter={onFilter}
onClose={() => setDocViewExpand(undefined)}
services={services}
/>
)}
</>
</DataGridContext.Provider>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { Fragment } from 'react';
import dompurify from 'dompurify';

import {
EuiDataGridCellValueElementProps,
EuiDescriptionList,
EuiDescriptionListTitle,
EuiDescriptionListDescription,
} from '@elastic/eui';
import { IndexPattern } from '../../../opensearch_dashboards_services';
import { OpenSearchSearchHit } from '../../doc_views/doc_views_types';

function fetchSourceTypeDataCell(
idxPattern: IndexPattern,
row: Record<string, unknown>,
columnId: string,
isDetails: boolean
) {
if (isDetails) {
return <span>{JSON.stringify(row[columnId], null, 2)}</span>;
}
const formattedRow = idxPattern.formatHit(row);

return (
<EuiDescriptionList type="inline" compressed>
{Object.keys(formattedRow).map((key) => (
<Fragment key={key}>
<EuiDescriptionListTitle>{key}</EuiDescriptionListTitle>
<EuiDescriptionListDescription
dangerouslySetInnerHTML={{ __html: dompurify.sanitize(formattedRow[key]) }}
/>
</Fragment>
))}
</EuiDescriptionList>
);
}

export const fetchTableDataCell = (
idxPattern: IndexPattern,
dataRows: OpenSearchSearchHit[] | undefined
) => ({ rowIndex, columnId, isDetails }: EuiDataGridCellValueElementProps) => {
const singleRow = dataRows ? (dataRows[rowIndex] as Record<string, unknown>) : undefined;
const flattenedRows = dataRows ? dataRows.map((hit) => idxPattern.flattenHit(hit)) : [];
const flattenedRow = flattenedRows
? (flattenedRows[rowIndex] as Record<string, unknown>)
: undefined;
const fieldInfo = idxPattern.fields.getByName(columnId);

if (typeof singleRow === 'undefined' || typeof flattenedRow === 'undefined') {
return <span>-</span>;
}

if (!fieldInfo?.type && flattenedRow && typeof flattenedRow[columnId] === 'object') {
if (isDetails) {
return <span>{JSON.stringify(flattenedRow[columnId], null, 2)}</span>;
}

return <span>{JSON.stringify(flattenedRow[columnId])}</span>;
}

if (fieldInfo?.type === '_source') {
return fetchSourceTypeDataCell(idxPattern, singleRow, columnId, isDetails);
}

const formattedValue = idxPattern.formatField(singleRow, columnId);
if (typeof formattedValue === 'undefined') {
return <span>-</span>;
} else {
const sanitizedCellValue = dompurify.sanitize(idxPattern.formatField(singleRow, columnId));
return (
// eslint-disable-next-line react/no-danger
<span dangerouslySetInnerHTML={{ __html: sanitizedCellValue }} />
);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { EuiDataGridColumn } from '@elastic/eui';
import { i18n } from '@osd/i18n';
import { IndexPattern } from '../../../opensearch_dashboards_services';

export function buildDataGridColumns(
columnNames: string[],
idxPattern: IndexPattern,
displayTimeColumn: boolean
) {
const timeFieldName = idxPattern.timeFieldName;
let columnsToUse = columnNames;

if (displayTimeColumn && idxPattern.timeFieldName && !columnNames.includes(timeFieldName)) {
columnsToUse = [idxPattern.timeFieldName, ...columnNames];
}

return columnsToUse.map((colName) => generateDataGridTableColumn(colName, idxPattern));
}

export function generateDataGridTableColumn(colName: string, idxPattern: IndexPattern) {
const timeLabel = i18n.translate('discover.timeLabel', {
defaultMessage: 'Time',
});
const idxPatternField = idxPattern.getFieldByName(colName);
const dataGridCol: EuiDataGridColumn = {
id: colName,
schema: idxPatternField?.type,
isSortable: idxPatternField?.sortable,
display: idxPatternField?.displayName,
actions: {
showHide: true,
showMoveLeft: false,
showMoveRight: false,
},
cellActions: [],
};

if (dataGridCol.id === idxPattern.timeFieldName) {
dataGridCol.display = `${timeLabel} (${idxPattern.timeFieldName})`;
dataGridCol.initialWidth = 200;
}
if (dataGridCol.id === '_source') {
dataGridCol.display = i18n.translate('discover.sourceLabel', {
defaultMessage: 'Source',
});
}
return dataGridCol;
}

export function computeVisibleColumns(
columnNames: string[],
idxPattern: IndexPattern,
displayTimeColumn: boolean
) {
const timeFieldName = idxPattern.timeFieldName;
let visibleColumnNames = columnNames;

if (displayTimeColumn && !columnNames.includes(timeFieldName)) {
visibleColumnNames = [timeFieldName, ...columnNames];
}

return visibleColumnNames;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { IndexPattern } from '../../../opensearch_dashboards_services';
import { DocViewFilterFn } from '../../doc_views/doc_views_types';

export interface DataGridContextProps {
docViewExpand: any;
onFilter: DocViewFilterFn;
setDocViewExpand: (hit: any) => void;
rows: any[];
indexPattern: IndexPattern;
}

export const DataGridContext = React.createContext<DataGridContextProps>(
({} as unknown) as DataGridContextProps
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useContext } from 'react';
import { EuiToolTip, EuiButtonIcon, EuiDataGridCellValueElementProps } from '@elastic/eui';
import { DataGridContext } from './data_grid_table_context';

export const DocViewExpandButton = ({
rowIndex,
setCellProps,
}: EuiDataGridCellValueElementProps) => {
const { docViewExpand, setDocViewExpand, rows } = useContext(DataGridContext);
const currentExpanded = rows[rowIndex];
const isCurrentExpanded = currentExpanded === docViewExpand;

return (
<EuiToolTip content={`Expand row ${rowIndex}`}>
<EuiButtonIcon
onClick={() => setDocViewExpand(isCurrentExpanded ? undefined : currentExpanded)}
iconType={isCurrentExpanded ? 'minimize' : 'expand'}
aria-label={`Expand row ${rowIndex}`}
/>
</EuiToolTip>
);
};
Loading

0 comments on commit 7d70608

Please sign in to comment.