From d13488a4352fd4ad141b0075b60591ed9986f8f9 Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Thu, 23 Mar 2023 13:28:45 +0000 Subject: [PATCH] NestedFolders: Show nested folders in Browse folder view (#63746) * dirty dirty code for showing nested folders in folder view refactor to NestedFolderItem Update dashboard grid view to new types update tests REBASE OUT OF THIS BRANCH - joshhunt/star-by-uid merged into this Squashed commit of the following: commit d0f046ccd3d820575f58d9a60cfcedf5cbdb217d Author: joshhunt Date: Wed Feb 8 18:35:56 2023 +0000 undo async commit abe2777a1fdb6418802102fbb1b6aca7ae4d8e66 Author: joshhunt Date: Wed Feb 8 18:34:11 2023 +0000 Dashboards: Star dashboards by UID add type for dashboard search dto clean DashboardSearchItem type simplify DashboardSearchHit type remove unused properties from DashboardSearchHit make uid non-optional rename + move NestedFolderItem type to DashboardViewItem clean up * wip * fix checkbox selection of nested folders * show folder's parent correctly * Add dashboard result kind * don't render folder empty view in SearchView * call nested folders api only if feature flag enabled * remove unused import * un-rename variable to reduce PR diff * fix typo in comment * fix order of pseudoFolders * Fix General folder not showing in browse * rename folder view tests --------- Co-authored-by: Ashley Harrison --- .../rule-editor/AnnotationsField.test.tsx | 1 + .../features/search/components/SearchItem.tsx | 30 ++++++++-- .../page/components/FolderSection.test.tsx | 2 +- .../search/page/components/FolderSection.tsx | 59 +++++++++++-------- ...rView.test.tsx => RootFolderView.test.tsx} | 56 ++++++++++++++---- .../{FolderView.tsx => RootFolderView.tsx} | 53 +++++++++-------- .../page/components/SearchResultsCards.tsx | 3 +- .../search/page/components/SearchView.tsx | 24 ++++++-- public/app/features/search/service/folders.ts | 52 ++++++++++++++++ public/app/features/search/service/types.ts | 5 ++ .../app/plugins/panel/dashlist/DashList.tsx | 2 +- public/locales/de-DE/grafana.json | 10 ++-- public/locales/en-US/grafana.json | 10 ++-- public/locales/es-ES/grafana.json | 10 ++-- public/locales/fr-FR/grafana.json | 10 ++-- public/locales/pseudo-LOCALE/grafana.json | 10 ++-- public/locales/zh-Hans/grafana.json | 10 ++-- 17 files changed, 250 insertions(+), 97 deletions(-) rename public/app/features/search/page/components/{FolderView.test.tsx => RootFolderView.test.tsx} (80%) rename public/app/features/search/page/components/{FolderView.tsx => RootFolderView.tsx} (75%) create mode 100644 public/app/features/search/service/folders.ts diff --git a/public/app/features/alerting/unified/components/rule-editor/AnnotationsField.test.tsx b/public/app/features/alerting/unified/components/rule-editor/AnnotationsField.test.tsx index 15258340ccd..db52f555dec 100644 --- a/public/app/features/alerting/unified/components/rule-editor/AnnotationsField.test.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/AnnotationsField.test.tsx @@ -255,6 +255,7 @@ function mockDashboardSearchItem(searchItem: Partial) { uri: '', items: [], tags: [], + slug: '', isStarred: false, ...searchItem, }; diff --git a/public/app/features/search/components/SearchItem.tsx b/public/app/features/search/components/SearchItem.tsx index ffe74c64dd1..ef106860a28 100644 --- a/public/app/features/search/components/SearchItem.tsx +++ b/public/app/features/search/components/SearchItem.tsx @@ -3,7 +3,9 @@ import React, { useCallback } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors'; +import { config } from '@grafana/runtime'; import { Card, Icon, IconName, TagList, useStyles2 } from '@grafana/ui'; +import { t } from 'app/core/internationalization'; import { SEARCH_ITEM_HEIGHT } from '../constants'; import { getIconForKind } from '../service/utils'; @@ -55,6 +57,16 @@ export const SearchItem = ({ item, isSelected, editable, onToggleChecked, onTagS [item, onToggleChecked] ); + const description = config.featureToggles.nestedFolders ? ( + <> + {kindName(item.kind)} + + ) : ( + <> + {item.parentTitle || 'General'} + + ); + return ( {item.title} + + - - - {item.parentTitle || 'General'} - + {description} {item.sortMetaName && ( @@ -92,6 +103,17 @@ export const SearchItem = ({ item, isSelected, editable, onToggleChecked, onTagS ); }; +function kindName(kind: DashboardViewItem['kind']) { + switch (kind) { + case 'folder': + return t('search.result-kind.folder', 'Folder'); + case 'dashboard': + return t('search.result-kind.dashboard', 'Dashboard'); + case 'panel': + return t('search.result-kind.panel', 'Panel'); + } +} + const getStyles = (theme: GrafanaTheme2) => { return { container: css` diff --git a/public/app/features/search/page/components/FolderSection.test.tsx b/public/app/features/search/page/components/FolderSection.test.tsx index 025e939d508..e22d99cc9a2 100644 --- a/public/app/features/search/page/components/FolderSection.test.tsx +++ b/public/app/features/search/page/components/FolderSection.test.tsx @@ -15,7 +15,7 @@ describe('FolderSection', () => { const mockSelectionToggle = jest.fn(); const mockSelection = jest.fn(); const mockSection: DashboardViewItem = { - kind: 'folder' as const, + kind: 'folder', uid: 'my-folder', title: 'My folder', }; diff --git a/public/app/features/search/page/components/FolderSection.tsx b/public/app/features/search/page/components/FolderSection.tsx index f40efa93db8..f5af53456f2 100644 --- a/public/app/features/search/page/components/FolderSection.tsx +++ b/public/app/features/search/page/components/FolderSection.tsx @@ -5,12 +5,15 @@ import { useAsync, useLocalStorage } from 'react-use'; import { GrafanaTheme2, toIconName } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { Card, Checkbox, CollapsableSection, Icon, Spinner, useStyles2 } from '@grafana/ui'; +import { config } from 'app/core/config'; import { t } from 'app/core/internationalization'; import { getSectionStorageKey } from 'app/features/search/utils'; import { useUniqueId } from 'app/plugins/datasource/influxdb/components/useUniqueId'; import { SearchItem } from '../..'; -import { getGrafanaSearcher, SearchQuery } from '../../service'; +import { GENERAL_FOLDER_UID } from '../../constants'; +import { getGrafanaSearcher } from '../../service'; +import { getFolderChildren } from '../../service/folders'; import { queryResultToViewItem } from '../../service/utils'; import { DashboardViewItem } from '../../types'; import { SelectionChecker, SelectionToggle } from '../selection'; @@ -25,6 +28,27 @@ interface SectionHeaderProps { tags?: string[]; } +async function getChildren(section: DashboardViewItem, tags: string[] | undefined): Promise { + if (config.featureToggles.nestedFolders) { + return getFolderChildren(section.uid, section.title); + } + + const query = section.itemsUIDs + ? { + uid: section.itemsUIDs, + } + : { + query: '*', + kind: ['dashboard'], + location: section.uid, + sort: 'name_sort', + limit: 1000, // this component does not have infinite scroll, so we need to load everything upfront + }; + + const raw = await getGrafanaSearcher().search({ ...query, tags }); + return raw.view.map((v) => queryResultToViewItem(v, raw.view)); +} + export const FolderSection = ({ section, selectionToggle, @@ -42,22 +66,10 @@ export const FolderSection = ({ if (!sectionExpanded && !renderStandaloneBody) { return Promise.resolve([]); } - let query: SearchQuery = { - query: '*', - kind: ['dashboard'], - location: section.uid, - sort: 'name_sort', - limit: 1000, // this component does not have infinate scroll, so we need to load everything upfront - }; - if (section.itemsUIDs) { - query = { - uid: section.itemsUIDs, // array of UIDs - }; - } - const raw = await getGrafanaSearcher().search({ ...query, tags }); - const items = raw.view.map((v) => queryResultToViewItem(v, raw.view)); - return items; + const childItems = getChildren(section, tags); + + return childItems; }, [sectionExpanded, tags]); const onSectionExpand = () => { @@ -72,8 +84,8 @@ export const FolderSection = ({ selectionToggle(section.kind, section.uid); const sub = results.value ?? []; for (const item of sub) { - if (selection('dashboard', item.uid!) !== checked) { - selectionToggle('dashboard', item.uid!); + if (selection(item.kind, item.uid!) !== checked) { + selectionToggle(item.kind, item.uid!); } } } @@ -104,14 +116,10 @@ export const FolderSection = ({ key={item.uid} item={item} onTagSelected={onTagSelected} - onToggleChecked={(item) => { - if (selectionToggle) { - selectionToggle('dashboard', item.uid!); - } - }} + onToggleChecked={(item) => selectionToggle?.(item.kind, item.uid)} editable={Boolean(selection != null)} onClickItem={onClickItem} - isSelected={selectionToggle && selection?.(item.kind, item.uid)} + isSelected={selection?.(item.kind, item.uid)} /> ); }); @@ -153,7 +161,7 @@ export const FolderSection = ({
{section.title} - {section.url && section.uid !== 'general' && ( + {section.url && section.uid !== GENERAL_FOLDER_UID && ( | {' '} {t('search.folder-view.go-to-folder', 'Go to folder')} @@ -170,6 +178,7 @@ export const FolderSection = ({ const getSectionHeaderStyles = (theme: GrafanaTheme2, editable: boolean) => { const sm = theme.spacing(1); + return { wrapper: css` align-items: center; diff --git a/public/app/features/search/page/components/FolderView.test.tsx b/public/app/features/search/page/components/RootFolderView.test.tsx similarity index 80% rename from public/app/features/search/page/components/FolderView.test.tsx rename to public/app/features/search/page/components/RootFolderView.test.tsx index f96139b6295..16fdd9c85f0 100644 --- a/public/app/features/search/page/components/FolderView.test.tsx +++ b/public/app/features/search/page/components/RootFolderView.test.tsx @@ -9,7 +9,7 @@ import impressionSrv from '../../../../core/services/impression_srv'; import { DashboardQueryResult, getGrafanaSearcher, QueryResponse } from '../../service'; import { DashboardSearchItemType } from '../../types'; -import { FolderView } from './FolderView'; +import { RootFolderView } from './RootFolderView'; jest.mock('@grafana/runtime', () => ({ ...jest.requireActual('@grafana/runtime'), @@ -18,7 +18,7 @@ jest.mock('@grafana/runtime', () => ({ }), })); -describe('FolderView', () => { +describe('RootFolderView', () => { let grafanaSearcherSpy: jest.SpyInstance; const mockOnTagSelected = jest.fn(); const mockSelectionToggle = jest.fn(); @@ -69,7 +69,11 @@ describe('FolderView', () => { grafanaSearcherSpy.mockImplementationOnce(() => promise); render( - + ); expect(await screen.findByTestId('Spinner')).toBeInTheDocument(); @@ -84,7 +88,11 @@ describe('FolderView', () => { it('does not show the starred items if not signed in', async () => { contextSrv.isSignedIn = false; render( - + ); expect((await screen.findAllByTestId(selectors.components.Search.sectionV2))[0]).toBeInTheDocument(); expect(screen.queryByRole('button', { name: 'Starred' })).not.toBeInTheDocument(); @@ -93,7 +101,11 @@ describe('FolderView', () => { it('shows the starred items if signed in', async () => { contextSrv.isSignedIn = true; render( - + ); expect(await screen.findByRole('button', { name: 'Starred' })).toBeInTheDocument(); }); @@ -101,7 +113,11 @@ describe('FolderView', () => { it('does not show the recent items if no dashboards have been opened recently', async () => { jest.spyOn(impressionSrv, 'getDashboardOpened').mockResolvedValue([]); render( - + ); expect((await screen.findAllByTestId(selectors.components.Search.sectionV2))[0]).toBeInTheDocument(); expect(screen.queryByRole('button', { name: 'Recent' })).not.toBeInTheDocument(); @@ -110,14 +126,22 @@ describe('FolderView', () => { it('shows the recent items if any dashboards have recently been opened', async () => { jest.spyOn(impressionSrv, 'getDashboardOpened').mockResolvedValue(['7MeksYbmk']); render( - + ); expect(await screen.findByRole('button', { name: 'Recent' })).toBeInTheDocument(); }); it('shows the general folder by default', async () => { render( - + ); expect(await screen.findByRole('button', { name: 'General' })).toBeInTheDocument(); }); @@ -126,7 +150,7 @@ describe('FolderView', () => { it('does not show the starred items even if signed in', async () => { contextSrv.isSignedIn = true; render( - { it('does not show the recent items even if recent dashboards have been opened', async () => { jest.spyOn(impressionSrv, 'getDashboardOpened').mockResolvedValue(['7MeksYbmk']); render( - { // reject with a specific Error object grafanaSearcherSpy.mockRejectedValueOnce(new Error('Uh oh spagghettios!')); render( - + ); expect(await screen.findByRole('alert', { name: 'Uh oh spagghettios!' })).toBeInTheDocument(); }); @@ -165,7 +193,11 @@ describe('FolderView', () => { // reject with nothing grafanaSearcherSpy.mockRejectedValueOnce(null); render( - + ); expect(await screen.findByRole('alert', { name: 'Something went wrong' })).toBeInTheDocument(); }); diff --git a/public/app/features/search/page/components/FolderView.tsx b/public/app/features/search/page/components/RootFolderView.tsx similarity index 75% rename from public/app/features/search/page/components/FolderView.tsx rename to public/app/features/search/page/components/RootFolderView.tsx index b68bc0ab1a4..1a311e0ac68 100644 --- a/public/app/features/search/page/components/FolderView.tsx +++ b/public/app/features/search/page/components/RootFolderView.tsx @@ -6,23 +6,39 @@ import { GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { getBackendSrv } from '@grafana/runtime'; import { Alert, Spinner, useStyles2 } from '@grafana/ui'; +import config from 'app/core/config'; import { contextSrv } from '../../../../core/services/context_srv'; import impressionSrv from '../../../../core/services/impression_srv'; import { GENERAL_FOLDER_UID } from '../../constants'; import { getGrafanaSearcher } from '../../service'; +import { getFolderChildren } from '../../service/folders'; import { queryResultToViewItem } from '../../service/utils'; -import { DashboardViewItem } from '../../types'; -import { SearchResultsProps } from '../components/SearchResultsTable'; import { FolderSection } from './FolderSection'; +import { SearchResultsProps } from './SearchResultsTable'; + +async function getChildren() { + if (config.featureToggles.nestedFolders) { + return getFolderChildren(); + } + + const searcher = getGrafanaSearcher(); + const results = await searcher.search({ + query: '*', + kind: ['folder'], + sort: searcher.getFolderViewSort(), + limit: 1000, + }); + + return results.view.map((v) => queryResultToViewItem(v, results.view)); +} type Props = Pick & { tags?: string[]; hidePseudoFolders?: boolean; }; - -export const FolderView = ({ +export const RootFolderView = ({ selection, selectionToggle, onTagSelected, @@ -33,33 +49,22 @@ export const FolderView = ({ const styles = useStyles2(getStyles); const results = useAsync(async () => { - const folders: DashboardViewItem[] = []; + const folders = await getChildren(); + + folders.unshift({ title: 'General', url: '/dashboards', kind: 'folder', uid: GENERAL_FOLDER_UID }); if (!hidePseudoFolders) { + const itemsUIDs = await impressionSrv.getDashboardOpened(); + if (itemsUIDs.length) { + folders.unshift({ title: 'Recent', icon: 'clock-nine', kind: 'folder', uid: '__recent', itemsUIDs }); + } + if (contextSrv.isSignedIn) { const stars = await getBackendSrv().get('api/user/stars'); if (stars.length > 0) { - folders.push({ title: 'Starred', icon: 'star', kind: 'folder', uid: '__starred', itemsUIDs: stars }); + folders.unshift({ title: 'Starred', icon: 'star', kind: 'folder', uid: '__starred', itemsUIDs: stars }); } } - - const itemsUIDs = await impressionSrv.getDashboardOpened(); - if (itemsUIDs.length) { - folders.push({ title: 'Recent', icon: 'clock-nine', kind: 'folder', uid: '__recent', itemsUIDs }); - } - } - - folders.push({ title: 'General', url: '/dashboards', kind: 'folder', uid: GENERAL_FOLDER_UID }); - - const searcher = getGrafanaSearcher(); - const results = await searcher.search({ - query: '*', - kind: ['folder'], - sort: searcher.getFolderViewSort(), - limit: 1000, - }); - for (const row of results.view) { - folders.push(queryResultToViewItem(row, results.view)); } return folders; diff --git a/public/app/features/search/page/components/SearchResultsCards.tsx b/public/app/features/search/page/components/SearchResultsCards.tsx index 3b6d9791fd4..0bd0c509001 100644 --- a/public/app/features/search/page/components/SearchResultsCards.tsx +++ b/public/app/features/search/page/components/SearchResultsCards.tsx @@ -48,6 +48,7 @@ export const SearchResultsCards = React.memo( const item = response.view.get(rowIndex); const searchItem = queryResultToViewItem(item, response.view); + const isSelected = selectionToggle && selection?.(searchItem.kind, searchItem.uid); return (
@@ -61,7 +62,7 @@ export const SearchResultsCards = React.memo( }} editable={Boolean(selection != null)} onClickItem={onClickItem} - isSelected={selectionToggle && selection?.(searchItem.kind, searchItem.uid)} + isSelected={isSelected} />
); diff --git a/public/app/features/search/page/components/SearchView.tsx b/public/app/features/search/page/components/SearchView.tsx index 5e84e778812..ce46c4910fd 100644 --- a/public/app/features/search/page/components/SearchView.tsx +++ b/public/app/features/search/page/components/SearchView.tsx @@ -5,6 +5,7 @@ import AutoSizer from 'react-virtualized-auto-sizer'; import { Observable } from 'rxjs'; import { GrafanaTheme2 } from '@grafana/data'; +import { config } from '@grafana/runtime'; import { useStyles2, Spinner, Button } from '@grafana/ui'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import { FolderDTO } from 'app/types'; @@ -12,13 +13,13 @@ import { FolderDTO } from 'app/types'; import { PreviewsSystemRequirements } from '../../components/PreviewsSystemRequirements'; import { getGrafanaSearcher } from '../../service'; import { getSearchStateManager } from '../../state/SearchStateManager'; -import { SearchLayout } from '../../types'; +import { SearchLayout, DashboardViewItem } from '../../types'; import { newSearchSelection, updateSearchSelection } from '../selection'; import { ActionRow, getValidQueryLayout } from './ActionRow'; import { FolderSection } from './FolderSection'; -import { FolderView } from './FolderView'; import { ManageActions } from './ManageActions'; +import { RootFolderView } from './RootFolderView'; import { SearchResultsCards } from './SearchResultsCards'; import { SearchResultsGrid } from './SearchResultsGrid'; import { SearchResultsTable, SearchResultsProps } from './SearchResultsTable'; @@ -86,11 +87,12 @@ export const SearchView = ({ showManage, folderDTO, hidePseudoFolders, keyboardE } const selection = showManage ? searchSelection.isSelected : undefined; + if (layout === SearchLayout.Folders) { if (folderDTO) { return ( ({ margin-top: ${theme.v1.spacing.md}; `, }); + +function sectionForFolderView(folderDTO: FolderDTO): DashboardViewItem { + return { uid: folderDTO.uid, kind: 'folder', title: folderDTO.title }; +} diff --git a/public/app/features/search/service/folders.ts b/public/app/features/search/service/folders.ts new file mode 100644 index 00000000000..c71609a21de --- /dev/null +++ b/public/app/features/search/service/folders.ts @@ -0,0 +1,52 @@ +import { getBackendSrv } from '@grafana/runtime'; +import config from 'app/core/config'; + +import { DashboardViewItem } from '../types'; + +import { getGrafanaSearcher } from './searcher'; +import { NestedFolderDTO } from './types'; +import { queryResultToViewItem } from './utils'; + +export async function getFolderChildren(parentUid?: string, parentTitle?: string): Promise { + if (!config.featureToggles.nestedFolders) { + console.error('getFolderChildren requires nestedFolders feature toggle'); + return []; + } + + if (!parentUid) { + // We don't show dashboards at root in folder view yet - they're shown under a dummy 'general' + // folder that FolderView adds in + const folders = await getChildFolders(); + return folders; + } + + const searcher = getGrafanaSearcher(); + const dashboardsResults = await searcher.search({ + kind: ['dashboard'], + query: '*', + location: parentUid, + limit: 1000, + }); + + const dashboardItems = dashboardsResults.view.map((item) => { + return queryResultToViewItem(item, dashboardsResults.view); + }); + + const folders = await getChildFolders(parentUid, parentTitle); + + return [...folders, ...dashboardItems]; +} + +async function getChildFolders(parentUid?: string, parentTitle?: string): Promise { + const backendSrv = getBackendSrv(); + + const folders = await backendSrv.get('/api/folders', { parentUid }); + + return folders.map((item) => ({ + kind: 'folder', + uid: item.uid, + title: item.title, + parentTitle, + url: `/dashboards/f/${item.uid}/`, + })); +} diff --git a/public/app/features/search/service/types.ts b/public/app/features/search/service/types.ts index 4b4fd0414d8..0f2b2fc8387 100644 --- a/public/app/features/search/service/types.ts +++ b/public/app/features/search/service/types.ts @@ -81,3 +81,8 @@ export interface GrafanaSearcher { /** Gets the default sort used for the Folder view */ getFolderViewSort: () => string; } + +export interface NestedFolderDTO { + uid: string; + title: string; +} diff --git a/public/app/plugins/panel/dashlist/DashList.tsx b/public/app/plugins/panel/dashlist/DashList.tsx index 5b16d30b2c5..c291c9a29d9 100644 --- a/public/app/plugins/panel/dashlist/DashList.tsx +++ b/public/app/plugins/panel/dashlist/DashList.tsx @@ -170,7 +170,7 @@ export function DashList(props: PanelProps) {
    {dashboards.map((dash) => (
  • - +
  • ))}
diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json index 0dbd03671c9..5d351256eda 100644 --- a/public/locales/de-DE/grafana.json +++ b/public/locales/de-DE/grafana.json @@ -9,8 +9,7 @@ "action": { "change-theme": "", "dark-theme": "", - "light-theme": "", - "search": "" + "light-theme": "" }, "search-box": { "placeholder": "" @@ -84,7 +83,6 @@ }, "toolbar": { "add-panel": "Panel hinzufügen", - "comments-show": "Dashboard-Kommentare anzeigen", "mark-favorite": "Als Favorit markieren", "open-original": "Original-Dashboard öffnen", "playlist-next": "Zum nächsten Dashboard", @@ -292,7 +290,6 @@ "title": "Szenen" }, "search": { - "placeholder": "Grafana durchsuchen", "placeholderCommandPalette": "" }, "search-dashboards": { @@ -415,6 +412,11 @@ "go-to-folder": "", "select-folder": "" }, + "result-kind": { + "dashboard": "", + "folder": "", + "panel": "" + }, "results-table": { "datasource-header": "", "location-header": "", diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 087a8e366b4..9ab2407332b 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -9,8 +9,7 @@ "action": { "change-theme": "Change theme...", "dark-theme": "Dark", - "light-theme": "Light", - "search": "Search" + "light-theme": "Light" }, "search-box": { "placeholder": "Search or jump to..." @@ -84,7 +83,6 @@ }, "toolbar": { "add-panel": "Add panel", - "comments-show": "Show dashboard comments", "mark-favorite": "Mark as favorite", "open-original": "Open original dashboard", "playlist-next": "Go to next dashboard", @@ -292,7 +290,6 @@ "title": "Scenes" }, "search": { - "placeholder": "Search Grafana", "placeholderCommandPalette": "Search or jump to..." }, "search-dashboards": { @@ -415,6 +412,11 @@ "go-to-folder": "Go to folder", "select-folder": "Select folder" }, + "result-kind": { + "dashboard": "Dashboard", + "folder": "Folder", + "panel": "Panel" + }, "results-table": { "datasource-header": "Data source", "location-header": "Location", diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json index b6c63f557ae..9ade0752373 100644 --- a/public/locales/es-ES/grafana.json +++ b/public/locales/es-ES/grafana.json @@ -9,8 +9,7 @@ "action": { "change-theme": "", "dark-theme": "", - "light-theme": "", - "search": "" + "light-theme": "" }, "search-box": { "placeholder": "" @@ -84,7 +83,6 @@ }, "toolbar": { "add-panel": "Añadir panel", - "comments-show": "Mostrar comentarios del panel de control", "mark-favorite": "Marcar como favorito", "open-original": "Abrir el panel de control original", "playlist-next": "Ir al siguiente panel de control", @@ -292,7 +290,6 @@ "title": "Escenas" }, "search": { - "placeholder": "Buscar Grafana", "placeholderCommandPalette": "" }, "search-dashboards": { @@ -415,6 +412,11 @@ "go-to-folder": "", "select-folder": "" }, + "result-kind": { + "dashboard": "", + "folder": "", + "panel": "" + }, "results-table": { "datasource-header": "", "location-header": "", diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json index 5d797e718c3..4ab94a23dc8 100644 --- a/public/locales/fr-FR/grafana.json +++ b/public/locales/fr-FR/grafana.json @@ -9,8 +9,7 @@ "action": { "change-theme": "", "dark-theme": "", - "light-theme": "", - "search": "" + "light-theme": "" }, "search-box": { "placeholder": "" @@ -84,7 +83,6 @@ }, "toolbar": { "add-panel": "Ajouter un panneau", - "comments-show": "Afficher les commentaires du tableau de bord", "mark-favorite": "Marquer comme favori", "open-original": "Ouvrir le tableau de bord d'origine", "playlist-next": "Accéder au tableau de bord suivant", @@ -292,7 +290,6 @@ "title": "Scènes" }, "search": { - "placeholder": "Rechercher dans Grafana", "placeholderCommandPalette": "" }, "search-dashboards": { @@ -415,6 +412,11 @@ "go-to-folder": "", "select-folder": "" }, + "result-kind": { + "dashboard": "", + "folder": "", + "panel": "" + }, "results-table": { "datasource-header": "", "location-header": "", diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index 5e7abf345bb..b7868e67d3d 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -9,8 +9,7 @@ "action": { "change-theme": "Cĥäʼnģę ŧĥęmę...", "dark-theme": "Đäřĸ", - "light-theme": "Ŀįģĥŧ", - "search": "Ŝęäřčĥ" + "light-theme": "Ŀįģĥŧ" }, "search-box": { "placeholder": "Ŝęäřčĥ őř ĵūmp ŧő..." @@ -84,7 +83,6 @@ }, "toolbar": { "add-panel": "Åđđ päʼnęľ", - "comments-show": "Ŝĥőŵ đäşĥþőäřđ čőmmęʼnŧş", "mark-favorite": "Mäřĸ äş ƒävőřįŧę", "open-original": "Øpęʼn őřįģįʼnäľ đäşĥþőäřđ", "playlist-next": "Ğő ŧő ʼnęχŧ đäşĥþőäřđ", @@ -292,7 +290,6 @@ "title": "Ŝčęʼnęş" }, "search": { - "placeholder": "Ŝęäřčĥ Ğřäƒäʼnä", "placeholderCommandPalette": "Ŝęäřčĥ őř ĵūmp ŧő..." }, "search-dashboards": { @@ -415,6 +412,11 @@ "go-to-folder": "Ğő ŧő ƒőľđęř", "select-folder": "Ŝęľęčŧ ƒőľđęř" }, + "result-kind": { + "dashboard": "Đäşĥþőäřđ", + "folder": "Főľđęř", + "panel": "Päʼnęľ" + }, "results-table": { "datasource-header": "Đäŧä şőūřčę", "location-header": "Ŀőčäŧįőʼn", diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json index 054e357f5ce..b7fdf50cea4 100644 --- a/public/locales/zh-Hans/grafana.json +++ b/public/locales/zh-Hans/grafana.json @@ -9,8 +9,7 @@ "action": { "change-theme": "", "dark-theme": "", - "light-theme": "", - "search": "" + "light-theme": "" }, "search-box": { "placeholder": "" @@ -84,7 +83,6 @@ }, "toolbar": { "add-panel": "添加面板", - "comments-show": "显示仪表板备注", "mark-favorite": "标记为收藏", "open-original": "打开原始仪表板", "playlist-next": "前往下一个仪表板", @@ -292,7 +290,6 @@ "title": "场景" }, "search": { - "placeholder": "搜索 Grafana", "placeholderCommandPalette": "" }, "search-dashboards": { @@ -415,6 +412,11 @@ "go-to-folder": "", "select-folder": "" }, + "result-kind": { + "dashboard": "", + "folder": "", + "panel": "" + }, "results-table": { "datasource-header": "", "location-header": "",