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 index c8eb08b0b082..06281eb68f55 100644 --- 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 @@ -11,7 +11,6 @@ import { buildDataGridColumns, computeVisibleColumns } from './data_grid_table_c import { DocViewInspectButton } from './data_grid_table_docview_inspect_button'; import { DataGridFlyout } from './data_grid_table_flyout'; import { DiscoverGridContextProvider } from './data_grid_table_context'; -import { toolbarVisibility } from './constants'; import { DocViewFilterFn, OpenSearchSearchHit } from '../../doc_views/doc_views_types'; import { usePagination } from '../utils/use_pagination'; import { SortOrder } from '../../../saved_searches/types'; @@ -19,7 +18,9 @@ import { buildColumns } from '../../utils/columns'; import { useOpenSearchDashboards } from '../../../../../opensearch_dashboards_react/public'; import { DiscoverServices } from '../../../build_services'; import { SAMPLE_SIZE_SETTING } from '../../../../common'; -import { DefaultDiscoverTable } from '../default_discover_table/default_discover_table'; +import { LegacyDiscoverTable } from '../default_discover_table/default_discover_table'; +import { toolbarVisibility } from './constants'; +import { getDataGridTableSetting } from '../utils/local_storage'; export interface DataGridTableProps { columns: string[]; @@ -38,6 +39,7 @@ export interface DataGridTableProps { isToolbarVisible?: boolean; isContextView?: boolean; isLoading?: boolean; + storage: any; } export const DataGridTable = ({ @@ -57,6 +59,7 @@ export const DataGridTable = ({ isToolbarVisible = true, isContextView = false, isLoading = false, + storage, }: DataGridTableProps) => { const { services } = useOpenSearchDashboards(); @@ -140,24 +143,11 @@ export const DataGridTable = ({ ]; }, []); - console.log("sortOrders", sortingColumns) + console.log('sortOrders', sortingColumns); - const table = useMemo( + const legacyDiscoverTable = useMemo( () => ( - // - ( - // - // ), - // [ - // displayedTableColumns, - // dataGridTableColumnsVisibility, - // leadingControlColumns, - // pagination, - // renderCellValue, - // rowCount, - // sorting, - // isToolbarVisible, - // rowHeightsOptions, - // ] - // ); + const dataGridTable = useMemo( + () => ( + + ), + [ + displayedTableColumns, + dataGridTableColumnsVisibility, + leadingControlColumns, + pagination, + renderCellValue, + rowCount, + sorting, + isToolbarVisible, + rowHeightsOptions, + ] + ); console.log('adjustColumns higher level', adjustedColumns); return ( @@ -233,7 +223,7 @@ export const DataGridTable = ({ data-test-subj="discoverTable" > - {table} + {getDataGridTableSetting(storage) ? dataGridTable : legacyDiscoverTable} {inspectedHit && ( ; onChangeSortOrder: (cols: EuiDataGridSorting['columns']) => void; onRemoveColumn: (column: string) => void; onReorderColumn: (col: string, source: number, destination: number) => void; @@ -30,7 +30,7 @@ export interface DefaultDiscoverTableProps { onClose: () => void; } -export const DefaultDiscoverTable = ({ +export const LegacyDiscoverTable = ({ displayedTableColumns, columns, rows, diff --git a/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.tsx b/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.tsx index 29c19887412c..df550e40f203 100644 --- a/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.tsx +++ b/src/plugins/discover/public/application/components/top_nav/get_top_nav_links.tsx @@ -22,6 +22,9 @@ import { getSortForSearchSource } from '../../view_components/utils/get_sort_for import { getRootBreadcrumbs } from '../../helpers/breadcrumbs'; import { syncQueryStateWithUrl } from '../../../../../data/public'; +import { showTableFeedbacksPanel } from './show_table_feedbacks_panel'; +import { getDataGridTableSetting, setDataGridTableSetting } from '../utils/local_storage'; + export const getTopNavLinks = ( services: DiscoverViewServices, inspectorAdapters: Adapters, @@ -38,6 +41,7 @@ export const getTopNavLinks = ( store, data: { query }, osdUrlStateStorage, + storage, } = services; const newSearch = { @@ -218,7 +222,35 @@ export const getTopNavLinks = ( }, }; + const newTable: TopNavMenuData = { + id: 'table-datagrid', + label: i18n.translate('discover.localMenu.newTableTitle', { + defaultMessage: 'Try new Discover experience', + }), + description: i18n.translate('discover.localMenu.newTableDescription', { + defaultMessage: 'New Data Grid Table Experience', + }), + testId: 'datagridTableButton', + run: async () => { + // Read the current state from localStorage + const useDatagridTable = getDataGridTableSetting(storage); + if (useDatagridTable) { + showTableFeedbacksPanel({ + I18nContext: core.i18n.Context, + services, + }); + } else { + // Save the new setting to localStorage + setDataGridTableSetting(true, storage); + window.location.reload(); + } + }, + type: 'toggle' as const, + emphasize: getDataGridTableSetting(storage), + }; + return [ + newTable, newSearch, ...(capabilities.discover?.save ? [saveSearch] : []), openSearch, @@ -278,7 +310,7 @@ const getSharingData = async ({ const searchSourceInstance = searchSource.createCopy(); const indexPattern = await searchSourceInstance.getField('index'); - const { searchFields, selectFields } = await getSharingDataFields( + const { searchFields } = await getSharingDataFields( state.columns, services.uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING), indexPattern?.timeFieldName diff --git a/src/plugins/discover/public/application/components/top_nav/show_table_feedbacks_panel.tsx b/src/plugins/discover/public/application/components/top_nav/show_table_feedbacks_panel.tsx new file mode 100644 index 000000000000..50db0cf5b6fa --- /dev/null +++ b/src/plugins/discover/public/application/components/top_nav/show_table_feedbacks_panel.tsx @@ -0,0 +1,51 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { I18nStart } from '../../../../../../core/public'; +import { OpenSearchDashboardsContextProvider } from '../../../../../opensearch_dashboards_react/public'; +import { DiscoverViewServices } from '../../../build_services'; +import { setDataGridTableSetting } from '../utils/local_storage'; +import { TableFeedbacksPanel } from './table_feedbacks_panel'; +let isFeedbackPanelOpen = false; + +export function showTableFeedbacksPanel({ + I18nContext, + services, +}: { + I18nContext: I18nStart['Context']; + services: DiscoverViewServices; +}) { + if (isFeedbackPanelOpen) { + return; + } + + isFeedbackPanelOpen = true; + const container = document.createElement('div'); + const onClose = () => { + ReactDOM.unmountComponentAtNode(container); + document.body.removeChild(container); + isFeedbackPanelOpen = false; + }; + + const onTurnOff = async () => { + // Save the new setting to localStorage + setDataGridTableSetting(false, services.storage); + onClose(); + window.location.reload(); + }; + + document.body.appendChild(container); + const element = ( + + + + + + ); + ReactDOM.render(element, container); +} diff --git a/src/plugins/discover/public/application/components/top_nav/table_feedbacks_panel.test.tsx b/src/plugins/discover/public/application/components/top_nav/table_feedbacks_panel.test.tsx new file mode 100644 index 000000000000..72e08dc61670 --- /dev/null +++ b/src/plugins/discover/public/application/components/top_nav/table_feedbacks_panel.test.tsx @@ -0,0 +1,34 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { shallow, ShallowWrapper } from 'enzyme'; +import { TableFeedbacksPanel } from './table_feedbacks_panel'; + +describe('TableFeedbacksPanel', () => { + let onCloseMock: jest.Mock; + let onTurnOffMock: jest.Mock; + let wrapper: ShallowWrapper; + + beforeEach(() => { + onCloseMock = jest.fn(); + onTurnOffMock = jest.fn(); + wrapper = shallow(); + }); + + it('renders correctly', () => { + expect(wrapper).toMatchSnapshot(); + }); + + it('calls onClose when the Cancel button is clicked', () => { + wrapper.find('EuiButtonEmpty').simulate('click'); + expect(onCloseMock).toHaveBeenCalled(); + }); + + it('calls onTurnOff when the Turn off new features button is clicked', () => { + wrapper.find('EuiButton').simulate('click'); + expect(onTurnOffMock).toHaveBeenCalled(); + }); +}); diff --git a/src/plugins/discover/public/application/components/top_nav/table_feedbacks_panel.tsx b/src/plugins/discover/public/application/components/top_nav/table_feedbacks_panel.tsx new file mode 100644 index 000000000000..fafc7160ab06 --- /dev/null +++ b/src/plugins/discover/public/application/components/top_nav/table_feedbacks_panel.tsx @@ -0,0 +1,48 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { + EuiModal, + EuiModalHeader, + EuiModalHeaderTitle, + EuiModalBody, + EuiModalFooter, + EuiButton, + EuiText, + EuiButtonEmpty, +} from '@elastic/eui'; + +interface TableFeedbacksPanelProps { + onClose: () => void; + onTurnOff: () => Promise; +} + +export const TableFeedbacksPanel = ({ onClose, onTurnOff }: TableFeedbacksPanelProps) => ( + + + +

Share your thoughts on the latest Discover features

+
+
+ + + +

+ Event tables: Documents are now expanded through a flyout. Density, column order, and + sorting controls have been improved.{' '} + Provide feedbacks. +

+
+
+ + + Cancel + + Turn off new features + + +
+); diff --git a/src/plugins/discover/public/application/components/utils/local_storage.ts b/src/plugins/discover/public/application/components/utils/local_storage.ts new file mode 100644 index 000000000000..ae2d8218c3c6 --- /dev/null +++ b/src/plugins/discover/public/application/components/utils/local_storage.ts @@ -0,0 +1,17 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Storage } from '../../../../../opensearch_dashboards_utils/public'; + +export const DATA_GRID_TABLE_KEY = 'discover:dataGridTable'; + +export const getDataGridTableSetting = (storage: Storage): boolean => { + const storedValue = storage.get(DATA_GRID_TABLE_KEY); + return storedValue !== null ? JSON.parse(storedValue) : false; +}; + +export const setDataGridTableSetting = (value: boolean, storage: Storage) => { + storage.set(DATA_GRID_TABLE_KEY, JSON.stringify(value)); +}; diff --git a/src/plugins/discover/public/application/view_components/canvas/discover_table.tsx b/src/plugins/discover/public/application/view_components/canvas/discover_table.tsx index 223dc0855359..918d42ee87f3 100644 --- a/src/plugins/discover/public/application/view_components/canvas/discover_table.tsx +++ b/src/plugins/discover/public/application/view_components/canvas/discover_table.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, useState, useEffect } from 'react'; import { DiscoverViewServices } from '../../../build_services'; import { useOpenSearchDashboards } from '../../../../../opensearch_dashboards_react/public'; import { DataGridTable } from '../../components/data_grid/data_grid_table'; @@ -37,6 +37,7 @@ export const DiscoverTable = ({ rows }: Props) => { }, capabilities, indexPatterns, + storage, } = services; const { refetch$, indexPattern, savedSearch } = useDiscoverContext(); @@ -66,7 +67,7 @@ export const DiscoverTable = ({ rows }: Props) => { const onSetColumns = (cols: string[]) => dispatch(setColumns({ columns: cols })); const onSetSort = (s: SortOrder[]) => { - console.log("sorting now!") + console.log('sorting now!'); dispatch(setSort(s)); refetch$.next(); }; @@ -115,6 +116,7 @@ export const DiscoverTable = ({ rows }: Props) => { displayTimeColumn={displayTimeColumn} title={savedSearch?.id ? savedSearch.title : ''} description={savedSearch?.id ? savedSearch.description : ''} + storage={storage} /> ); }; diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index 785e72536417..36a8908594f0 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -58,6 +58,7 @@ import { OpenSearchDashboardsLegacyStart } from '../../opensearch_dashboards_leg import { UrlForwardingStart } from '../../url_forwarding/public'; import { NavigationPublicPluginStart } from '../../navigation/public'; import { DataExplorerServices } from '../../data_explorer/public'; +import { Storage } from '../../opensearch_dashboards_utils/public'; export interface DiscoverServices { addBasePath: (path: string) => string; @@ -82,6 +83,7 @@ export interface DiscoverServices { getSavedSearchUrlById: (id: string) => Promise; uiSettings: IUiSettingsClient; visualizations: VisualizationsStart; + storage: Storage; } export function buildServices( @@ -97,6 +99,7 @@ export function buildServices( overlays: core.overlays, }; const savedObjectService = createSavedSearchesLoader(services); + const storage = new Storage(localStorage); return { addBasePath: core.http.basePath.prepend, @@ -123,6 +126,7 @@ export function buildServices( toastNotifications: core.notifications.toasts, uiSettings: core.uiSettings, visualizations: plugins.visualizations, + storage, }; } diff --git a/src/plugins/discover/public/embeddable/search_embeddable_component.tsx b/src/plugins/discover/public/embeddable/search_embeddable_component.tsx index 97df2e5c45b7..ebf8bb778026 100644 --- a/src/plugins/discover/public/embeddable/search_embeddable_component.tsx +++ b/src/plugins/discover/public/embeddable/search_embeddable_component.tsx @@ -12,6 +12,7 @@ import { DataGridTableProps, } from '../application/components/data_grid/data_grid_table'; import { VisualizationNoResults } from '../../../visualizations/public'; +import { getServices } from '../opensearch_dashboards_services'; import './search_embeddable.scss'; interface SearchEmbeddableProps { @@ -26,6 +27,7 @@ export const DataGridTableMemoized = React.memo((props: DataGridTableProps) => ( )); export function SearchEmbeddableComponent({ searchProps }: SearchEmbeddableProps) { + const { storage } = getServices(); const discoverEmbeddableProps = { columns: searchProps.columns, indexPattern: searchProps.indexPattern, @@ -41,6 +43,7 @@ export function SearchEmbeddableComponent({ searchProps }: SearchEmbeddableProps totalHitCount: searchProps.totalHitCount, title: searchProps.title, description: searchProps.description, + storage, } as DiscoverEmbeddableProps; return (