From d9b4aa07f7b982ba00c39b528c91e1d2dcbbc172 Mon Sep 17 00:00:00 2001 From: Artur Wierzbicki Date: Fri, 7 Apr 2023 19:15:46 +0400 Subject: [PATCH] Previews: remove dashboard previews UI (#66146) * remove dashboard previews ui * remove dashboard previews ui * remove layout prop * remove layout prop --- .../components/PreviewsSystemRequirements.tsx | 94 ------------ .../search/page/components/ActionRow.tsx | 17 +-- .../components/SearchResultsGrid.test.tsx | 66 --------- .../page/components/SearchResultsGrid.tsx | 134 ------------------ .../search/page/components/SearchView.tsx | 13 -- public/app/features/search/page/reporting.ts | 7 +- public/app/features/search/types.ts | 1 - .../app/plugins/panel/dashlist/DashList.tsx | 17 +-- public/app/plugins/panel/dashlist/module.tsx | 17 +-- .../app/plugins/panel/dashlist/panelcfg.cue | 2 - .../plugins/panel/dashlist/panelcfg.gen.ts | 7 - 11 files changed, 7 insertions(+), 368 deletions(-) delete mode 100644 public/app/features/search/components/PreviewsSystemRequirements.tsx delete mode 100644 public/app/features/search/page/components/SearchResultsGrid.test.tsx delete mode 100644 public/app/features/search/page/components/SearchResultsGrid.tsx diff --git a/public/app/features/search/components/PreviewsSystemRequirements.tsx b/public/app/features/search/components/PreviewsSystemRequirements.tsx deleted file mode 100644 index c3ba284c01b..00000000000 --- a/public/app/features/search/components/PreviewsSystemRequirements.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { css } from '@emotion/css'; -import React from 'react'; - -import { config } from '@grafana/runtime/src'; -import { Alert, useStyles2 } from '@grafana/ui'; - -export interface Props { - showPreviews?: boolean; - /** On click handler for alert button, mostly used for dismissing the alert */ - onRemove?: (event: React.MouseEvent) => void; - topSpacing?: number; - bottomSpacing?: number; -} - -const MessageLink = ({ text }: { text: string }) => ( - - {text} - -); - -const Message = ({ requiredImageRendererPluginVersion }: { requiredImageRendererPluginVersion?: string }) => { - if (requiredImageRendererPluginVersion) { - return ( - <> - You must update the to version{' '} - {requiredImageRendererPluginVersion} to enable dashboard previews. Please contact your Grafana administrator to - update the plugin. - - ); - } - - return ( - <> - You must install the to enable dashboard previews. Please - contact your Grafana administrator to install the plugin. - - ); -}; - -export const PreviewsSystemRequirements = ({ showPreviews, onRemove, topSpacing, bottomSpacing }: Props) => { - const styles = useStyles2(getStyles); - - const previewsEnabled = config.featureToggles.dashboardPreviews; - const rendererAvailable = config.rendererAvailable; - - const { - systemRequirements: { met: systemRequirementsMet, requiredImageRendererPluginVersion }, - thumbnailsExist, - } = config.dashboardPreviews; - - const arePreviewsEnabled = previewsEnabled && showPreviews; - const areRequirementsMet = (rendererAvailable && systemRequirementsMet) || thumbnailsExist; - const shouldDisplayRequirements = arePreviewsEnabled && !areRequirementsMet; - - const title = requiredImageRendererPluginVersion - ? 'Image renderer plugin needs to be updated' - : 'Image renderer plugin not installed'; - - return ( - <> - {shouldDisplayRequirements && ( -
- - - -
- )} - - ); -}; - -const getStyles = () => { - return { - wrapper: css` - display: flex; - justify-content: center; - `, - alert: css` - max-width: 800px; - `, - }; -}; diff --git a/public/app/features/search/page/components/ActionRow.tsx b/public/app/features/search/page/components/ActionRow.tsx index 74400e7847c..f9881c9351f 100644 --- a/public/app/features/search/page/components/ActionRow.tsx +++ b/public/app/features/search/page/components/ActionRow.tsx @@ -3,7 +3,7 @@ import React, { FormEvent } from 'react'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { config } from '@grafana/runtime'; -import { HorizontalGroup, RadioButtonGroup, useStyles2, Checkbox, Button } from '@grafana/ui'; +import { Button, Checkbox, HorizontalGroup, RadioButtonGroup, useStyles2 } from '@grafana/ui'; import { SortPicker } from 'app/core/components/Select/SortPicker'; import { TagFilter, TermCount } from 'app/core/components/TagFilter/TagFilter'; import { t, Trans } from 'app/core/internationalization'; @@ -11,20 +11,10 @@ import { t, Trans } from 'app/core/internationalization'; import { SearchLayout, SearchState } from '../../types'; function getLayoutOptions() { - const layoutOptions = [ + return [ { value: SearchLayout.Folders, icon: 'folder', ariaLabel: t('search.actions.view-as-folders', 'View by folders') }, { value: SearchLayout.List, icon: 'list-ul', ariaLabel: t('search.actions.view-as-list', 'View as list') }, ]; - - if (config.featureToggles.dashboardPreviews) { - layoutOptions.push({ - value: SearchLayout.Grid, - icon: 'apps', - ariaLabel: t('search.actions.view-as-grid', 'Grid view'), - }); - } - - return layoutOptions; } interface Props { @@ -54,9 +44,6 @@ export function getValidQueryLayout(q: SearchState): SearchLayout { } } - if (layout === SearchLayout.Grid && !config.featureToggles.dashboardPreviews) { - return SearchLayout.List; - } return layout; } diff --git a/public/app/features/search/page/components/SearchResultsGrid.test.tsx b/public/app/features/search/page/components/SearchResultsGrid.test.tsx deleted file mode 100644 index 05eb1187051..00000000000 --- a/public/app/features/search/page/components/SearchResultsGrid.test.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; -import React, { KeyboardEvent } from 'react'; -import { Observable } from 'rxjs'; - -import { ArrayVector, DataFrame, DataFrameView, FieldType } from '@grafana/data'; -import { selectors } from '@grafana/e2e-selectors'; - -import { DashboardQueryResult, QueryResponse } from '../../service'; -import { DashboardSearchItemType } from '../../types'; - -import { SearchResultsGrid } from './SearchResultsGrid'; - -describe('SearchResultsGrid', () => { - const dashboardsData: DataFrame = { - fields: [ - { - name: 'kind', - type: FieldType.string, - config: {}, - values: new ArrayVector([DashboardSearchItemType.DashDB]), - }, - { name: 'name', type: FieldType.string, config: {}, values: new ArrayVector(['My dashboard 1', 'dash2']) }, - { name: 'uid', type: FieldType.string, config: {}, values: new ArrayVector(['my-dashboard-1', 'dash-2']) }, - { name: 'url', type: FieldType.string, config: {}, values: new ArrayVector(['/my-dashbaord-1', '/dash-2']) }, - ], - length: 2, - }; - const mockSearchResult: QueryResponse = { - isItemLoaded: jest.fn(), - loadMoreItems: jest.fn(), - totalRows: dashboardsData.length, - view: new DataFrameView(dashboardsData), - }; - - const baseProps = { - response: mockSearchResult, - width: 800, - height: 600, - clearSelection: jest.fn(), - onTagSelected: jest.fn(), - keyboardEvents: new Observable>(), - }; - - it('should render grid of dashboards', () => { - render(); - expect(screen.getByTestId(selectors.components.Search.dashboardCard('My dashboard 1'))).toBeInTheDocument(); - expect(screen.getByTestId(selectors.components.Search.dashboardCard('dash2'))).toBeInTheDocument(); - }); - - it('should not render checkboxes for non-editable results', async () => { - render(); - expect(screen.queryByRole('checkbox')).toBeNull(); - await waitFor(() => expect(screen.queryAllByRole('checkbox')).toHaveLength(0)); - }); - - it('should render checkboxes for editable results ', async () => { - const mockSelectionToggle = jest.fn(); - const mockSelection = jest.fn(); - render(); - - await waitFor(() => expect(screen.queryAllByRole('checkbox')).toHaveLength(2)); - fireEvent.click(await screen.findByRole('checkbox', { name: /Select dashboard dash2/i })); - expect(mockSelectionToggle).toHaveBeenCalledWith('dashboard', 'dash-2'); - expect(mockSelectionToggle).toHaveBeenCalledTimes(1); - }); -}); diff --git a/public/app/features/search/page/components/SearchResultsGrid.tsx b/public/app/features/search/page/components/SearchResultsGrid.tsx deleted file mode 100644 index 037fe739886..00000000000 --- a/public/app/features/search/page/components/SearchResultsGrid.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import { css } from '@emotion/css'; -import React from 'react'; -import { FixedSizeGrid } from 'react-window'; -import InfiniteLoader from 'react-window-infinite-loader'; - -import { GrafanaTheme2 } from '@grafana/data'; -import { config } from '@grafana/runtime'; -import { useStyles2 } from '@grafana/ui'; - -import { SearchCard } from '../../components/SearchCard'; -import { useSearchKeyboardNavigation } from '../../hooks/useSearchKeyboardSelection'; -import { queryResultToViewItem } from '../../service/utils'; -import { DashboardViewItem } from '../../types'; - -import { SearchResultsProps } from './SearchResultsTable'; - -export const SearchResultsGrid = ({ - response, - width, - height, - selection, - selectionToggle, - onTagSelected, - onClickItem, - keyboardEvents, -}: SearchResultsProps) => { - const styles = useStyles2(getStyles); - - // Hacked to reuse existing SearchCard (and old DashboardSectionItem) - const itemProps = { - editable: selection != null, - onToggleChecked: (item: DashboardViewItem) => { - if (selectionToggle) { - selectionToggle(item.kind, item.uid); - } - }, - onTagSelected, - onClick: onClickItem, - }; - - const itemCount = response.totalRows ?? response.view.length; - const view = response.view; - const numColumns = Math.ceil(width / 320); - const cellWidth = width / numColumns; - const cellHeight = (cellWidth - 64) * 0.75 + 56 + 8; - const numRows = Math.ceil(itemCount / numColumns); - const highlightIndex = useSearchKeyboardNavigation(keyboardEvents, numColumns, response); - - return ( - - {({ onItemsRendered, ref }) => ( - { - onItemsRendered({ - visibleStartIndex: v.visibleRowStartIndex * numColumns, - visibleStopIndex: v.visibleRowStopIndex * numColumns, - overscanStartIndex: v.overscanRowStartIndex * numColumns, - overscanStopIndex: v.overscanColumnStopIndex * numColumns, - }); - }} - columnCount={numColumns} - columnWidth={cellWidth} - rowCount={numRows} - rowHeight={cellHeight} - className={styles.wrapper} - innerElementType="ul" - height={height} - width={width - 2} - > - {({ columnIndex, rowIndex, style }) => { - const index = rowIndex * numColumns + columnIndex; - if (index >= view.length) { - return null; - } - const item = view.get(index); - const kind = item.kind ?? 'dashboard'; - - const facade = queryResultToViewItem(item, view); - - if (kind === 'panel') { - const type = item.panel_type; - facade.icon = 'public/img/icons/unicons/graph-bar.svg'; - if (type) { - const info = config.panels[type]; - if (info?.name) { - const v = info.info?.logos.small; - if (v && v.endsWith('.svg')) { - facade.icon = v; - } - } - } - } - - let className = styles.virtualizedGridItemWrapper; - if (rowIndex === highlightIndex.y && columnIndex === highlightIndex.x) { - className += ' ' + styles.selectedItem; - } - - // The wrapper div is needed as the inner SearchItem has margin-bottom spacing - // And without this wrapper there is no room for that margin - return item ? ( -
  • - -
  • - ) : null; - }} -
    - )} -
    - ); -}; - -const getStyles = (theme: GrafanaTheme2) => ({ - virtualizedGridItemWrapper: css` - padding: 4px; - `, - wrapper: css` - display: flex; - flex-direction: column; - - > ul { - list-style: none; - } - `, - selectedItem: css` - box-shadow: inset 1px 1px 3px 3px ${theme.colors.primary.border}; - `, -}); diff --git a/public/app/features/search/page/components/SearchView.tsx b/public/app/features/search/page/components/SearchView.tsx index ce46c4910fd..b1f65393187 100644 --- a/public/app/features/search/page/components/SearchView.tsx +++ b/public/app/features/search/page/components/SearchView.tsx @@ -10,7 +10,6 @@ import { useStyles2, Spinner, Button } from '@grafana/ui'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import { FolderDTO } from 'app/types'; -import { PreviewsSystemRequirements } from '../../components/PreviewsSystemRequirements'; import { getGrafanaSearcher } from '../../service'; import { getSearchStateManager } from '../../state/SearchStateManager'; import { SearchLayout, DashboardViewItem } from '../../types'; @@ -21,7 +20,6 @@ import { FolderSection } from './FolderSection'; import { ManageActions } from './ManageActions'; import { RootFolderView } from './RootFolderView'; import { SearchResultsCards } from './SearchResultsCards'; -import { SearchResultsGrid } from './SearchResultsGrid'; import { SearchResultsTable, SearchResultsProps } from './SearchResultsTable'; export type SearchViewProps = { @@ -133,10 +131,6 @@ export const SearchView = ({ showManage, folderDTO, hidePseudoFolders, keyboardE onClickItem: stateManager.onSearchItemClicked, }; - if (layout === SearchLayout.Grid) { - return ; - } - if (width < 800) { return ; } @@ -193,13 +187,6 @@ export const SearchView = ({ showManage, folderDTO, hidePseudoFolders, keyboardE /> )} - {layout === SearchLayout.Grid && ( - stateManager.onLayoutChange(SearchLayout.List)} - /> - )} {renderResults()} ); diff --git a/public/app/features/search/page/reporting.ts b/public/app/features/search/page/reporting.ts index 9c708ac069f..5b89b070c2e 100644 --- a/public/app/features/search/page/reporting.ts +++ b/public/app/features/search/page/reporting.ts @@ -1,4 +1,4 @@ -import { config, reportInteraction } from '@grafana/runtime'; +import { reportInteraction } from '@grafana/runtime'; import { InspectTab } from 'app/features/inspector/types'; import { EventTrackingNamespace, SearchLayout } from '../types'; @@ -40,12 +40,7 @@ export const reportPanelInspectInteraction = ( }; const getQuerySearchContext = (query: QueryProps) => { - const showPreviews = query.layout === SearchLayout.Grid; - const previewsEnabled = Boolean(config.featureToggles.panelTitleSearch); - const previews = previewsEnabled ? (showPreviews ? 'on' : 'off') : 'feature_disabled'; - return { - previews, layout: query.layout, starredFilter: query.starred ?? false, sort: query.sortValue ?? '', diff --git a/public/app/features/search/types.ts b/public/app/features/search/types.ts index 269727fa88f..d0f148da767 100644 --- a/public/app/features/search/types.ts +++ b/public/app/features/search/types.ts @@ -102,7 +102,6 @@ export type OnToggleChecked = (item: DashboardViewItem) => void; export enum SearchLayout { List = 'list', Folders = 'folders', - Grid = 'grid', // preview } export interface SearchQueryParams { diff --git a/public/app/plugins/panel/dashlist/DashList.tsx b/public/app/plugins/panel/dashlist/DashList.tsx index 102b0cc1f6b..c776990ea44 100644 --- a/public/app/plugins/panel/dashlist/DashList.tsx +++ b/public/app/plugins/panel/dashlist/DashList.tsx @@ -20,12 +20,11 @@ import { getBackendSrv } from 'app/core/services/backend_srv'; import impressionSrv from 'app/core/services/impression_srv'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; -import { SearchCard } from 'app/features/search/components/SearchCard'; import { DashboardSearchItem } from 'app/features/search/types'; import { getVariablesUrlParams } from 'app/features/variables/getAllVariableValuesForUrl'; import { useDispatch } from 'app/types'; -import { PanelLayout, PanelOptions } from './panelcfg.gen'; +import { PanelOptions } from './panelcfg.gen'; import { getStyles } from './styles'; type Dashboard = DashboardSearchItem & { id?: number; isSearchResult?: boolean; isRecent?: boolean }; @@ -130,7 +129,7 @@ export function DashList(props: PanelProps) { ]; }, [dashboards]); - const { showStarred, showRecentlyViewed, showHeadings, showSearch, layout } = props.options; + const { showStarred, showRecentlyViewed, showHeadings, showSearch } = props.options; const dashboardGroups: DashboardGroup[] = [ { @@ -198,16 +197,6 @@ export function DashList(props: PanelProps) { ); - const renderPreviews = (dashboards: Dashboard[]) => ( -
      - {dashboards.map((dash) => ( -
    • - -
    • - ))} -
    - ); - return ( {dashboardGroups.map( @@ -215,7 +204,7 @@ export function DashList(props: PanelProps) { show && (
    {showHeadings &&
    {header}
    } - {layout === PanelLayout.Previews ? renderPreviews(dashboards) : renderList(dashboards)} + {renderList(dashboards)}
    ) )} diff --git a/public/app/plugins/panel/dashlist/module.tsx b/public/app/plugins/panel/dashlist/module.tsx index d22c00e5094..19e5a85c64a 100644 --- a/public/app/plugins/panel/dashlist/module.tsx +++ b/public/app/plugins/panel/dashlist/module.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { PanelModel, PanelPlugin } from '@grafana/data'; -import { config } from '@grafana/runtime'; import { TagsInput } from '@grafana/ui'; import { @@ -11,24 +10,10 @@ import { } from '../../../core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker'; import { DashList } from './DashList'; -import { defaultPanelOptions, PanelLayout, PanelOptions } from './panelcfg.gen'; +import { defaultPanelOptions, PanelOptions } from './panelcfg.gen'; export const plugin = new PanelPlugin(DashList) .setPanelOptions((builder) => { - if (config.featureToggles.dashboardPreviews) { - builder.addRadio({ - path: 'layout', - name: 'Layout', - defaultValue: PanelLayout.List, - settings: { - options: [ - { value: PanelLayout.List, label: 'List' }, - { value: PanelLayout.Previews, label: 'Preview' }, - ], - }, - }); - } - builder .addBooleanSwitch({ path: 'keepTime', diff --git a/public/app/plugins/panel/dashlist/panelcfg.cue b/public/app/plugins/panel/dashlist/panelcfg.cue index 600563bbf27..bd656a5e174 100644 --- a/public/app/plugins/panel/dashlist/panelcfg.cue +++ b/public/app/plugins/panel/dashlist/panelcfg.cue @@ -22,9 +22,7 @@ composableKinds: PanelCfg: { { schemas: [ { - PanelLayout: "list" | "previews" @cuetsy(kind="enum") PanelOptions: { - layout?: PanelLayout | *"list" keepTime: bool | *false includeVars: bool | *false showStarred: bool | *true diff --git a/public/app/plugins/panel/dashlist/panelcfg.gen.ts b/public/app/plugins/panel/dashlist/panelcfg.gen.ts index 19f89096fd7..a884e896d2b 100644 --- a/public/app/plugins/panel/dashlist/panelcfg.gen.ts +++ b/public/app/plugins/panel/dashlist/panelcfg.gen.ts @@ -10,16 +10,10 @@ export const PanelCfgModelVersion = Object.freeze([0, 0]); -export enum PanelLayout { - List = 'list', - Previews = 'previews', -} - export interface PanelOptions { folderId?: number; includeVars: boolean; keepTime: boolean; - layout?: PanelLayout; maxItems: number; query: string; showHeadings: boolean; @@ -32,7 +26,6 @@ export interface PanelOptions { export const defaultPanelOptions: Partial = { includeVars: false, keepTime: false, - layout: PanelLayout.List, maxItems: 10, query: '', showHeadings: true,