diff --git a/src/plugins/discover/public/application/components/data_grid/constants.ts b/src/plugins/discover/public/application/components/data_grid/constants.ts new file mode 100644 index 000000000000..c11f205aff1f --- /dev/null +++ b/src/plugins/discover/public/application/components/data_grid/constants.ts @@ -0,0 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export const toolbarVisibility = { + showColumnSelector: { + allowHide: false, + allowReorder: true, + }, + showStyleSelector: false, +}; diff --git a/src/plugins/discover/public/application/components/data_grid/data_grid_table.scss b/src/plugins/discover/public/application/components/data_grid/data_grid_table.scss new file mode 100644 index 000000000000..23c329dbcdec --- /dev/null +++ b/src/plugins/discover/public/application/components/data_grid/data_grid_table.scss @@ -0,0 +1,9 @@ +.dscDiscoverGrid { + height: 100%; + width: 100%; + overflow: hidden; + + .euiDataGrid__controls { + border: $euiBorderThin; + } +} diff --git a/src/plugins/discover/public/application/components/data_grid/data_grid_table.tsx b/src/plugins/discover/public/application/components/data_grid/data_grid_table.tsx new file mode 100644 index 000000000000..0767e6991608 --- /dev/null +++ b/src/plugins/discover/public/application/components/data_grid/data_grid_table.tsx @@ -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 ( + + <> + + {docViewExpand && ( + setDocViewExpand(undefined)} + services={services} + /> + )} + + + ); +}; diff --git a/src/plugins/discover/public/application/components/data_grid/data_grid_table_cell_value.tsx b/src/plugins/discover/public/application/components/data_grid/data_grid_table_cell_value.tsx new file mode 100644 index 000000000000..2e39dde3ba06 --- /dev/null +++ b/src/plugins/discover/public/application/components/data_grid/data_grid_table_cell_value.tsx @@ -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, + columnId: string, + isDetails: boolean +) { + if (isDetails) { + return {JSON.stringify(row[columnId], null, 2)}; + } + const formattedRow = idxPattern.formatHit(row); + + return ( + + {Object.keys(formattedRow).map((key) => ( + + {key} + + + ))} + + ); +} + +export const fetchTableDataCell = ( + idxPattern: IndexPattern, + dataRows: OpenSearchSearchHit[] | undefined +) => ({ rowIndex, columnId, isDetails }: EuiDataGridCellValueElementProps) => { + const singleRow = dataRows ? (dataRows[rowIndex] as Record) : undefined; + const flattenedRows = dataRows ? dataRows.map((hit) => idxPattern.flattenHit(hit)) : []; + const flattenedRow = flattenedRows + ? (flattenedRows[rowIndex] as Record) + : undefined; + const fieldInfo = idxPattern.fields.getByName(columnId); + + if (typeof singleRow === 'undefined' || typeof flattenedRow === 'undefined') { + return -; + } + + if (!fieldInfo?.type && flattenedRow && typeof flattenedRow[columnId] === 'object') { + if (isDetails) { + return {JSON.stringify(flattenedRow[columnId], null, 2)}; + } + + return {JSON.stringify(flattenedRow[columnId])}; + } + + if (fieldInfo?.type === '_source') { + return fetchSourceTypeDataCell(idxPattern, singleRow, columnId, isDetails); + } + + const formattedValue = idxPattern.formatField(singleRow, columnId); + if (typeof formattedValue === 'undefined') { + return -; + } else { + const sanitizedCellValue = dompurify.sanitize(idxPattern.formatField(singleRow, columnId)); + return ( + // eslint-disable-next-line react/no-danger + + ); + } +}; diff --git a/src/plugins/discover/public/application/components/data_grid/data_grid_table_columns.tsx b/src/plugins/discover/public/application/components/data_grid/data_grid_table_columns.tsx new file mode 100644 index 000000000000..1561a7e838da --- /dev/null +++ b/src/plugins/discover/public/application/components/data_grid/data_grid_table_columns.tsx @@ -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; +} diff --git a/src/plugins/discover/public/application/components/data_grid/data_grid_table_context.tsx b/src/plugins/discover/public/application/components/data_grid/data_grid_table_context.tsx new file mode 100644 index 000000000000..55561119b225 --- /dev/null +++ b/src/plugins/discover/public/application/components/data_grid/data_grid_table_context.tsx @@ -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( + ({} as unknown) as DataGridContextProps +); diff --git a/src/plugins/discover/public/application/components/data_grid/data_grid_table_docview_expand_button.tsx b/src/plugins/discover/public/application/components/data_grid/data_grid_table_docview_expand_button.tsx new file mode 100644 index 000000000000..30aa4e45d0c7 --- /dev/null +++ b/src/plugins/discover/public/application/components/data_grid/data_grid_table_docview_expand_button.tsx @@ -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 ( + + setDocViewExpand(isCurrentExpanded ? undefined : currentExpanded)} + iconType={isCurrentExpanded ? 'minimize' : 'expand'} + aria-label={`Expand row ${rowIndex}`} + /> + + ); +}; diff --git a/src/plugins/discover/public/application/components/data_grid/data_grid_table_flyout.tsx b/src/plugins/discover/public/application/components/data_grid/data_grid_table_flyout.tsx new file mode 100644 index 000000000000..c75ef8a3e6c8 --- /dev/null +++ b/src/plugins/discover/public/application/components/data_grid/data_grid_table_flyout.tsx @@ -0,0 +1,120 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { i18n } from '@osd/i18n'; +import { stringify } from 'query-string'; +import rison from 'rison-node'; + +import { + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiLink, + EuiSpacer, +} from '@elastic/eui'; +import { DocViewer } from '../doc_viewer/doc_viewer'; +import { IndexPattern } from '../../../opensearch_dashboards_services'; +import { DocViewFilterFn } from '../../doc_views/doc_views_types'; +import { DiscoverServices } from '../../../build_services'; +import { url } from '../../../../../opensearch_dashboards_utils/common'; +import { opensearchFilters } from '../../../../../data/public'; + +interface Props { + columns: string[]; + hit: any; + indexPattern: IndexPattern; + onAddColumn: (column: string) => void; + onClose: () => void; + onFilter: DocViewFilterFn; + onRemoveColumn: (column: string) => void; + services: DiscoverServices; +} + +export function DataGridFlyout({ + hit, + columns, + indexPattern, + onAddColumn, + onClose, + onFilter, + onRemoveColumn, + services, +}: Props) { + const generateSurroundingDocumentsUrl = (hitId: string, indexPatternId: string) => { + const globalFilters = services.filterManager.getGlobalFilters(); + const appFilters = services.filterManager.getAppFilters(); + + const hash = stringify( + url.encodeQuery({ + _g: rison.encode({ + filters: globalFilters || [], + }), + _a: rison.encode({ + columns, + filters: (appFilters || []).map(opensearchFilters.disableFilter), + }), + }), + { encode: false, sort: false } + ); + + return `#/context/${encodeURIComponent(indexPatternId)}/${encodeURIComponent(hitId)}?${hash}`; + }; + + const generateSingleDocumentUrl = (hitObj: any, indexPatternId: string) => { + return `#/doc/${indexPatternId}/${hitObj._index}?id=${encodeURIComponent(hit._id)}`; + }; + + return ( + + + +

Document Details

+
+ + + + + {i18n.translate('discover.docTable.tableRow.viewSingleDocumentLinkText', { + defaultMessage: 'View single document', + })} + + + + + {i18n.translate('discover.docTable.tableRow.viewSurroundingDocumentsLinkText', { + defaultMessage: 'View surrounding documents', + })} + + + +
+ + + { + onRemoveColumn(columnName); + onClose(); + }} + onAddColumn={(columnName: string) => { + onAddColumn(columnName); + onClose(); + }} + filter={(mapping, value, mode) => { + onFilter(mapping, value, mode); + onClose(); + }} + /> + + +
+ ); +} diff --git a/src/plugins/discover/public/application/components/discover.scss b/src/plugins/discover/public/application/components/discover.scss new file mode 100644 index 000000000000..aaa5a4c5c90d --- /dev/null +++ b/src/plugins/discover/public/application/components/discover.scss @@ -0,0 +1,33 @@ +.dscAppContainer { + flex-direction: column; + flex-grow: 1; + overflow: hidden; + + > * { + position: relative; + } +} + +.dscTimechart { + display: block; + position: relative; + + // SASSTODO: the visualizing component should have an option or a modifier + .series > rect { + fill-opacity: 0.5; + stroke-width: 1; + } +} + +.dscTimechart__header { + display: flex; + justify-content: center; + min-height: $euiSizeXXL; + padding: $euiSizeXS 0; +} + +.dscHistogram { + display: flex; + height: 200px; + padding: $euiSizeS; +} diff --git a/src/plugins/discover/public/application/components/discover.tsx b/src/plugins/discover/public/application/components/discover.tsx new file mode 100644 index 000000000000..b9cd40f5fc2b --- /dev/null +++ b/src/plugins/discover/public/application/components/discover.tsx @@ -0,0 +1,317 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import './discover.scss'; +import React, { useState, useCallback, useEffect } from 'react'; +import { + EuiPage, + EuiPageBody, + EuiButtonEmpty, + EuiButtonIcon, + EuiPageSideBar, + EuiPageContent, + EuiFlexGroup, + EuiFlexItem, +} from '@elastic/eui'; +import { i18n } from '@osd/i18n'; +import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; +import { IUiSettingsClient, MountPoint } from 'opensearch-dashboards/public'; +import { HitsCounter } from './hits_counter'; +import { TimechartHeader } from './timechart_header'; +import { DiscoverSidebar } from './sidebar'; +import { DataGridTable } from './data_grid/data_grid_table'; +import { getServices, IndexPattern } from '../../opensearch_dashboards_services'; +// @ts-ignore +import { DiscoverNoResults } from '../angular/directives/no_results'; +import { DiscoverUninitialized } from '../angular/directives/uninitialized'; +import { DiscoverHistogram } from '../angular/directives/histogram'; +import { LoadingSpinner } from './loading_spinner/loading_spinner'; +import { SkipBottomButton } from './skip_bottom_button'; +import { + IndexPatternField, + search, + ISearchSource, + TimeRange, + Query, + IndexPatternAttributes, +} from '../../../../data/public'; +import { Chart } from '../angular/helpers/point_series'; +import { AppState } from '../angular/discover_state'; +import { SavedSearch } from '../../saved_searches'; + +import { SavedObject } from '../../../../../core/types'; +import { Vis } from '../../../../visualizations/public'; +import { TopNavMenuData } from '../../../../navigation/public'; +import { DocViewFilterFn } from '../doc_views/doc_views_types'; + +export interface DiscoverProps { + addColumn: (column: string) => void; + fetch: () => void; + fetchCounter: number; + fieldCounts: Record; + histogramData: Chart; + hits: number; + indexPattern: IndexPattern; + onAddFilter: DocViewFilterFn; + onChangeInterval: (interval: string) => void; + onMoveColumn: (columns: string, newIdx: number) => void; + onRemoveColumn: (column: string) => void; + onSetColumns: (columns: string[]) => void; + onSkipBottomButtonClick: () => void; + onSort: (sort: string[][]) => void; + opts: { + savedSearch: SavedSearch; + config: IUiSettingsClient; + indexPatternList: Array>; + timefield: string; + sampleSize: number; + fixedScroll: (el: HTMLElement) => void; + setHeaderActionMenu: (menuMount: MountPoint | undefined) => void; + }; + resetQuery: () => void; + resultState: string; + rows: Array>; + searchSource: ISearchSource; + setIndexPattern: (id: string) => void; + showSaveQuery: boolean; + state: AppState; + timefilterUpdateHandler: (ranges: { from: number; to: number }) => void; + timeRange?: { from: string; to: string }; + topNavMenu: TopNavMenuData[]; + updateQuery: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void; + updateSavedQueryId: (savedQueryId?: string) => void; + vis?: Vis; +} + +export function Discover({ + addColumn, + fetch, + fetchCounter, + fieldCounts, + histogramData, + hits, + indexPattern, + onAddFilter, + onChangeInterval, + onMoveColumn, + onRemoveColumn, + onSkipBottomButtonClick, + onSetColumns, + onSort, + opts, + resetQuery, + resultState, + rows, + searchSource, + setIndexPattern, + showSaveQuery, + state, + timefilterUpdateHandler, + timeRange, + topNavMenu, + updateQuery, + updateSavedQueryId, + vis, +}: DiscoverProps) { + const [isSidebarClosed, setIsSidebarClosed] = useState(false); + const services = getServices(); + const { TopNavMenu } = services.navigation.ui; + const { savedSearch, indexPatternList, config } = opts; + const bucketAggConfig = vis?.data?.aggs?.aggs[1]; + const bucketInterval = + bucketAggConfig && search.aggs.isDateHistogramBucketAggConfig(bucketAggConfig) + ? bucketAggConfig.buckets?.getInterval() + : undefined; + const [fixedScrollEl, setFixedScrollEl] = useState(); + + useEffect(() => (fixedScrollEl ? opts.fixedScroll(fixedScrollEl) : undefined), [ + fixedScrollEl, + opts, + ]); + const fixedScrollRef = useCallback( + (node: HTMLElement) => { + if (node !== null) { + setFixedScrollEl(node); + } + }, + [setFixedScrollEl] + ); + const displayTimeColumn = Boolean( + !config.get('doc_table:hideTimeColumn', false) && indexPattern.timeFieldName + ); + + return ( + + +

{savedSearch.title}

+ + + + + + + + {!isSidebarClosed && ( +
+ +
+ )} + setIsSidebarClosed(!isSidebarClosed)} + data-test-subj="collapseSideBarButton" + aria-controls="discover-sidebar" + aria-expanded={isSidebarClosed ? 'false' : 'true'} + aria-label="Toggle sidebar" + className="dscCollapsibleSidebar__collapseButton euiButtonIcon--auto" + /> +
+
+ + + + {resultState === 'none' && ( + + )} + {resultState === 'uninitialized' && } + + {/* Loading State */} + {resultState === 'loading' && ( +
+ +
+ )} + + {/* Ready State */} + {resultState === 'ready' && ( +
+ + 0 ? hits : 0} + showResetButton={!!(savedSearch && savedSearch.id)} + onResetQuery={resetQuery} + /> + {opts.timefield && ( + + )} + + {opts.timefield && ( +
+ {vis && rows.length !== 0 && ( +
+ +
+ )} +
+ )} + +
+
+

+ +

+ {rows && rows.length && ( +
+ ) || []} + onAddColumn={addColumn} + onFilter={onAddFilter} + onRemoveColumn={onRemoveColumn} + onSetColumns={onSetColumns} + onSort={onSort} + displayTimeColumn={displayTimeColumn} + services={services} + /> + + ​ + + {rows.length === opts.sampleSize && ( +
+ + + window.scrollTo(0, 0)}> + + +
+ )} +
+ )} +
+
+
+ )} +
+
+
+
+
+
+ ); +} diff --git a/src/plugins/discover/public/application/components/doc_viewer_links/__snapshots__/doc_viewer_links.test.tsx.snap b/src/plugins/discover/public/application/components/doc_viewer_links/__snapshots__/doc_viewer_links.test.tsx.snap deleted file mode 100644 index 95fb0c377180..000000000000 --- a/src/plugins/discover/public/application/components/doc_viewer_links/__snapshots__/doc_viewer_links.test.tsx.snap +++ /dev/null @@ -1,34 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Dont Render if generateCb.hide 1`] = ` - -`; - -exports[`Render with 2 different links 1`] = ` - - - - - - - - -`; diff --git a/src/plugins/discover/public/application/components/doc_viewer_links/doc_viewer_links.test.tsx b/src/plugins/discover/public/application/components/doc_viewer_links/doc_viewer_links.test.tsx deleted file mode 100644 index 8aba555b3a37..000000000000 --- a/src/plugins/discover/public/application/components/doc_viewer_links/doc_viewer_links.test.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react'; -import { shallow } from 'enzyme'; -import { DocViewerLinks } from './doc_viewer_links'; -import { getDocViewsLinksRegistry } from '../../../opensearch_dashboards_services'; -import { DocViewLinkRenderProps } from '../../doc_views_links/doc_views_links_types'; - -jest.mock('../../../opensearch_dashboards_services', () => { - let registry: any[] = []; - return { - getDocViewsLinksRegistry: () => ({ - addDocViewLink(view: any) { - registry.push(view); - }, - getDocViewsLinksSorted() { - return registry; - }, - resetRegistry: () => { - registry = []; - }, - }), - }; -}); - -beforeEach(() => { - (getDocViewsLinksRegistry() as any).resetRegistry(); - jest.clearAllMocks(); -}); - -test('Render with 2 different links', () => { - const registry = getDocViewsLinksRegistry(); - registry.addDocViewLink({ - order: 10, - label: 'generateCb link', - generateCb: () => ({ - url: 'aaa', - }), - }); - registry.addDocViewLink({ order: 20, label: 'href link', href: 'bbb' }); - - const renderProps = { hit: {} } as DocViewLinkRenderProps; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); -}); - -test('Dont Render if generateCb.hide', () => { - const registry = getDocViewsLinksRegistry(); - registry.addDocViewLink({ - order: 10, - label: 'generateCb link', - generateCb: () => ({ - url: 'aaa', - hide: true, - }), - }); - - const renderProps = { hit: {} } as DocViewLinkRenderProps; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); -}); diff --git a/src/plugins/discover/public/application/components/doc_viewer_links/doc_viewer_links.tsx b/src/plugins/discover/public/application/components/doc_viewer_links/doc_viewer_links.tsx deleted file mode 100644 index 9efb0693fde6..000000000000 --- a/src/plugins/discover/public/application/components/doc_viewer_links/doc_viewer_links.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiListGroupItem, EuiListGroupItemProps } from '@elastic/eui'; -import { getDocViewsLinksRegistry } from '../../../opensearch_dashboards_services'; -import { DocViewLinkRenderProps } from '../../doc_views_links/doc_views_links_types'; - -export function DocViewerLinks(renderProps: DocViewLinkRenderProps) { - const listItems = getDocViewsLinksRegistry() - .getDocViewsLinksSorted() - .filter((item) => !(item.generateCb && item.generateCb(renderProps)?.hide)) - .map((item) => { - const { generateCb, href, ...props } = item; - const listItem: EuiListGroupItemProps = { - 'data-test-subj': 'docTableRowAction', - ...props, - href: generateCb ? generateCb(renderProps).url : href, - }; - - return listItem; - }); - - return ( - - {listItems.map((item, index) => ( - - - - ))} - - ); -} diff --git a/src/plugins/discover/public/application/components/utils/use_pagination.ts b/src/plugins/discover/public/application/components/utils/use_pagination.ts new file mode 100644 index 000000000000..98363e57ed95 --- /dev/null +++ b/src/plugins/discover/public/application/components/utils/use_pagination.ts @@ -0,0 +1,39 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { useState, useMemo, useCallback } from 'react'; + +export const usePagination = (rowCount: number) => { + const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 100 }); + const pageCount = useMemo(() => Math.ceil(rowCount / pagination.pageSize), [ + rowCount, + pagination, + ]); + + const onChangeItemsPerPage = useCallback( + (pageSize: number) => setPagination((p) => ({ ...p, pageSize })), + [] + ); + + const onChangePage = useCallback( + (pageIndex: number) => setPagination((p) => ({ ...p, pageIndex })), + [] + ); + + return useMemo( + () => + pagination.pageSize + ? { + ...pagination, + onChangeItemsPerPage, + onChangePage, + pageIndex: pagination.pageIndex > pageCount - 1 ? 0 : pagination.pageIndex, + pageSize: pagination.pageSize, + pageSizeOptions: [25, 50, 100], // TODO: make this configurable + } + : undefined, + [pagination, onChangeItemsPerPage, onChangePage, pageCount] + ); +}; diff --git a/src/plugins/discover/public/application/doc_views/doc_views_registry.ts b/src/plugins/discover/public/application/doc_views/doc_views_registry.ts index 56f167b5f2cc..904d3813cd69 100644 --- a/src/plugins/discover/public/application/doc_views/doc_views_registry.ts +++ b/src/plugins/discover/public/application/doc_views/doc_views_registry.ts @@ -28,32 +28,16 @@ * under the License. */ -import { auto } from 'angular'; -import { convertDirectiveToRenderFn } from './doc_views_helpers'; import { DocView, DocViewInput, OpenSearchSearchHit, DocViewInputFn } from './doc_views_types'; export class DocViewsRegistry { private docViews: DocView[] = []; - private angularInjectorGetter: (() => Promise) | null = null; - - setAngularInjectorGetter = (injectorGetter: () => Promise) => { - this.angularInjectorGetter = injectorGetter; - }; /** * Extends and adds the given doc view to the registry array */ addDocView(docViewRaw: DocViewInput | DocViewInputFn) { const docView = typeof docViewRaw === 'function' ? docViewRaw() : docViewRaw; - if (docView.directive) { - // convert angular directive to render function for backwards compatibility - docView.render = convertDirectiveToRenderFn(docView.directive, () => { - if (!this.angularInjectorGetter) { - throw new Error('Angular was not initialized'); - } - return this.angularInjectorGetter(); - }); - } if (typeof docView.shouldShow !== 'function') { docView.shouldShow = () => true; } diff --git a/src/plugins/discover/public/application/doc_views/doc_views_types.ts b/src/plugins/discover/public/application/doc_views/doc_views_types.ts index 961fc98516f6..bf027d32848a 100644 --- a/src/plugins/discover/public/application/doc_views/doc_views_types.ts +++ b/src/plugins/discover/public/application/doc_views/doc_views_types.ts @@ -72,7 +72,6 @@ export type DocViewRenderFn = ( export interface DocViewInput { component?: DocViewerComponent; - directive?: AngularDirective; order: number; render?: DocViewRenderFn; shouldShow?: (hit: OpenSearchSearchHit) => boolean; diff --git a/src/plugins/discover/public/application/doc_views_links/doc_views_links_registry.ts b/src/plugins/discover/public/application/doc_views_links/doc_views_links_registry.ts deleted file mode 100644 index 16653f5d5377..000000000000 --- a/src/plugins/discover/public/application/doc_views_links/doc_views_links_registry.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { DocViewLink } from './doc_views_links_types'; - -export class DocViewsLinksRegistry { - private docViewsLinks: DocViewLink[] = []; - - addDocViewLink(docViewLink: DocViewLink) { - this.docViewsLinks.push(docViewLink); - } - - getDocViewsLinksSorted() { - return this.docViewsLinks.sort((a, b) => (Number(a.order) > Number(b.order) ? 1 : -1)); - } -} diff --git a/src/plugins/discover/public/application/doc_views_links/doc_views_links_types.ts b/src/plugins/discover/public/application/doc_views_links/doc_views_links_types.ts deleted file mode 100644 index bbc5caadafcd..000000000000 --- a/src/plugins/discover/public/application/doc_views_links/doc_views_links_types.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { EuiListGroupItemProps } from '@elastic/eui'; -import { OpenSearchSearchHit } from '../doc_views/doc_views_types'; -import { IndexPattern } from '../../../../data/public'; - -export interface DocViewLink extends EuiListGroupItemProps { - href?: string; - order: number; - generateCb?( - renderProps: any - ): { - url: string; - hide?: boolean; - }; -} - -export interface DocViewLinkRenderProps { - columns?: string[]; - hit: OpenSearchSearchHit; - indexPattern: IndexPattern; -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_viewer_links.tsx b/src/plugins/discover_legacy/public/application/angular/doc_viewer_links.tsx deleted file mode 100644 index 763a75e51300..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_viewer_links.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react'; -import { DocViewerLinks } from '../components/doc_viewer_links/doc_viewer_links'; - -export function createDocViewerLinksDirective(reactDirective: any) { - return reactDirective( - (props: any) => { - return ; - }, - [ - 'hit', - ['indexPattern', { watchDepth: 'reference' }], - ['columns', { watchDepth: 'collection' }], - ], - { - restrict: 'E', - scope: { - hit: '=', - indexPattern: '=', - columns: '=?', - }, - } - ); -}