diff --git a/.betterer.results b/.betterer.results index 89141673931..cdf32535173 100644 --- a/.betterer.results +++ b/.betterer.results @@ -2513,9 +2513,6 @@ exports[`better eslint`] = { "public/app/features/dashboard-scene/settings/annotations/AnnotationSettingsEdit.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], - "public/app/features/dashboard-scene/settings/annotations/AnnotationSettingsList.tsx:5381": [ - [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] - ], "public/app/features/dashboard-scene/settings/annotations/index.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./AnnotationSettingsEdit\`)", "0"], [0, 0, 0, "Do not re-export imported variable (\`./AnnotationSettingsList\`)", "1"] @@ -2565,8 +2562,7 @@ exports[`better eslint`] = { [0, 0, 0, "Styles should be written using objects.", "5"] ], "public/app/features/dashboard/components/AnnotationSettings/AnnotationSettingsList.tsx:5381": [ - [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] + [0, 0, 0, "Styles should be written using objects.", "0"] ], "public/app/features/dashboard/components/AnnotationSettings/index.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./AnnotationSettingsEdit\`)", "0"], @@ -3923,7 +3919,7 @@ exports[`better eslint`] = { [0, 0, 0, "Styles should be written using objects.", "1"] ], "public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui/src\' is restricted from being used by a pattern. Use Stack component instead.", "0"], + [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], [0, 0, 0, "Styles should be written using objects.", "1"], [0, 0, 0, "Styles should be written using objects.", "2"], [0, 0, 0, "Styles should be written using objects.", "3"], diff --git a/public/app/features/alerting/unified/components/silences/NoSilencesCTA.tsx b/public/app/features/alerting/unified/components/silences/NoSilencesCTA.tsx index b754f5b4fdb..fd42325863a 100644 --- a/public/app/features/alerting/unified/components/silences/NoSilencesCTA.tsx +++ b/public/app/features/alerting/unified/components/silences/NoSilencesCTA.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import { CallToActionCard } from '@grafana/ui'; -import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; +import { CallToActionCard, EmptyState, LinkButton } from '@grafana/ui'; +import { Trans, t } from 'app/core/internationalization'; import { contextSrv } from 'app/core/services/context_srv'; import { getInstancesPermissions } from '../../utils/access-control'; @@ -16,11 +16,14 @@ export const NoSilencesSplash = ({ alertManagerSourceName }: Props) => { if (contextSrv.hasPermission(permissions.create)) { return ( - + Create silence + + } + message={t('silences.empty-state.title', "You haven't created any silences yet")} /> ); } diff --git a/public/app/features/browse-dashboards/components/BrowseView.test.tsx b/public/app/features/browse-dashboards/components/BrowseView.test.tsx index bdca23a807d..a083f8374c5 100644 --- a/public/app/features/browse-dashboards/components/BrowseView.test.tsx +++ b/public/app/features/browse-dashboards/components/BrowseView.test.tsx @@ -149,7 +149,7 @@ describe('browse-dashboards BrowseView', () => { describe('when there is no item in the folder', () => { it('shows a CTA for creating a dashboard if the user has editor rights', async () => { render(); - expect(await screen.findByText('Create Dashboard')).toBeInTheDocument(); + expect(await screen.findByText('Create dashboard')).toBeInTheDocument(); }); it('shows a simple message if the user has viewer rights', async () => { diff --git a/public/app/features/browse-dashboards/components/BrowseView.tsx b/public/app/features/browse-dashboards/components/BrowseView.tsx index 4c657da02db..b9cc1ea5a2d 100644 --- a/public/app/features/browse-dashboards/components/BrowseView.tsx +++ b/public/app/features/browse-dashboards/components/BrowseView.tsx @@ -1,7 +1,7 @@ import React, { useCallback } from 'react'; -import { CallToActionCard } from '@grafana/ui'; -import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; +import { CallToActionCard, EmptyState, LinkButton, TextLink } from '@grafana/ui'; +import { Trans, t } from 'app/core/internationalization'; import { DashboardViewItem } from 'app/features/search/types'; import { useDispatch } from 'app/types'; @@ -117,16 +117,32 @@ export function BrowseView({ folderUID, width, height, canSelect }: BrowseViewPr return (
{canSelect ? ( - '} - proTipLink={folderUID && 'dashboards'} - proTipLinkTitle={folderUID && 'Browse dashboards'} - proTipTarget="" - /> + + Create dashboard + + } + message={ + folderUID + ? t('browse-dashboards.empty-state.title-folder', "This folder doesn't have any dashboards yet") + : t('browse-dashboards.empty-state.title', "You haven't created any dashboards yet") + } + > + {folderUID && ( + + Add/move dashboards to your folder at{' '} + + Browse dashboards + + + )} + ) : ( This folder is empty} /> )} diff --git a/public/app/features/correlations/components/EmptyCorrelationsCTA.tsx b/public/app/features/correlations/components/EmptyCorrelationsCTA.tsx index 1228224ffc2..b4f020295ce 100644 --- a/public/app/features/correlations/components/EmptyCorrelationsCTA.tsx +++ b/public/app/features/correlations/components/EmptyCorrelationsCTA.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import { Card } from '@grafana/ui'; -import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; +import { Button, Card, EmptyState } from '@grafana/ui'; +import { Trans, t } from 'app/core/internationalization'; interface Props { onClick?: () => void; @@ -11,13 +11,19 @@ export const EmptyCorrelationsCTA = ({ onClick, canWriteCorrelations }: Props) = // TODO: if there are no datasources show a different message return canWriteCorrelations ? ( - + + Add correlation + + } + message={t('correlations.empty-state.title', "You haven't defined any correlations yet")} + > + + You can also define correlations via datasource provisioning + + ) : ( There are no correlations configured yet. diff --git a/public/app/features/dashboard-scene/settings/annotations/AnnotationSettingsList.test.tsx b/public/app/features/dashboard-scene/settings/annotations/AnnotationSettingsList.test.tsx index 3a9d6fc28bf..ab7a07b7393 100644 --- a/public/app/features/dashboard-scene/settings/annotations/AnnotationSettingsList.test.tsx +++ b/public/app/features/dashboard-scene/settings/annotations/AnnotationSettingsList.test.tsx @@ -65,21 +65,21 @@ describe('AnnotationSettingsEdit', () => { it('should render with empty list message', async () => { const { - renderer: { getByTestId }, + renderer: { getByRole }, } = await setup(true); - const emptyListBtn = getByTestId(selectors.components.CallToActionCard.buttonV2(BUTTON_TITLE)); + const emptyListBtn = getByRole('button', { name: BUTTON_TITLE }); expect(emptyListBtn).toBeInTheDocument(); }); it('should create new annotation when empty list button is pressed', async () => { const { - renderer: { getByTestId }, + renderer: { getByRole }, user, } = await setup(true); - const emptyListBtn = getByTestId(selectors.components.CallToActionCard.buttonV2(BUTTON_TITLE)); + const emptyListBtn = getByRole('button', { name: BUTTON_TITLE }); await user.click(emptyListBtn); diff --git a/public/app/features/dashboard-scene/settings/annotations/AnnotationSettingsList.tsx b/public/app/features/dashboard-scene/settings/annotations/AnnotationSettingsList.tsx index c36d12510d6..154851834e2 100644 --- a/public/app/features/dashboard-scene/settings/annotations/AnnotationSettingsList.tsx +++ b/public/app/features/dashboard-scene/settings/annotations/AnnotationSettingsList.tsx @@ -4,8 +4,8 @@ import React from 'react'; import { AnnotationQuery } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { getDataSourceSrv } from '@grafana/runtime'; -import { Button, DeleteButton, IconButton, useStyles2, VerticalGroup } from '@grafana/ui'; -import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; +import { Button, DeleteButton, EmptyState, IconButton, Stack, TextLink, useStyles2 } from '@grafana/ui'; +import { t, Trans } from 'app/core/internationalization'; import { ListNewButton } from 'app/features/dashboard/components/DashboardSettings/ListNewButton'; import { MoveDirection } from '../AnnotationsEditView'; @@ -39,7 +39,7 @@ export const AnnotationSettingsList = ({ annotations, onNew, onEdit, onMove, onD const dataSourceSrv = getDataSourceSrv(); return ( - + {annotations.length > 0 && (
@@ -99,25 +99,38 @@ export const AnnotationSettingsList = ({ annotations, onNew, onEdit, onMove, onD )} {showEmptyListCTA && ( - Annotations provide a way to integrate event data into your graphs. They are visualized as vertical lines - and icons on all graph panels. When you hover over an annotation icon you can get event text & tags for - the event. You can add annotation events directly from grafana by holding CTRL or CMD + click on graph (or - drag region). These will be stored in Grafana's annotation database. -

- Checkout the - Annotations documentation - for more information.`, - }} - /> + + + Add annotation query + + } + message={t('annotations.empty-state.title', 'There are no custom annotation queries added yet')} + > + +

+ Annotations provide a way to integrate event data into your graphs. They are visualized as vertical + lines and icons on all graph panels. When you hover over an annotation icon you can get event text & + tags for the event. You can add annotation events directly from grafana by holding CTRL or CMD + click + on graph (or drag region). These will be stored in Grafana's annotation database. +

+
+ + Checkout the{' '} + + Annotations documentation + {' '} + for more information. + +
+
)} {!showEmptyListCTA && ( )} - + ); }; diff --git a/public/app/features/dashboard-scene/settings/links/DashboardLinkList.tsx b/public/app/features/dashboard-scene/settings/links/DashboardLinkList.tsx index 9e9f76d3d15..d235eb7f7b7 100644 --- a/public/app/features/dashboard-scene/settings/links/DashboardLinkList.tsx +++ b/public/app/features/dashboard-scene/settings/links/DashboardLinkList.tsx @@ -3,8 +3,19 @@ import React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { DashboardLink } from '@grafana/schema'; -import { Button, DeleteButton, HorizontalGroup, Icon, IconButton, TagList, useStyles2 } from '@grafana/ui'; -import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; +import { + Button, + DeleteButton, + EmptyState, + HorizontalGroup, + Icon, + IconButton, + Stack, + TagList, + TextLink, + useStyles2, +} from '@grafana/ui'; +import { Trans, t } from 'app/core/internationalization'; interface DashboardLinkListProps { links: DashboardLink[]; @@ -28,19 +39,28 @@ export function DashboardLinkList({ if (isEmptyList) { return ( -
- Dashboard Links allow you to place links to other dashboards and web sites directly below the dashboard header.

', - }} - /> -
+ + + Add dashboard link + + } + message={t('dashboard-links.empty-state.title', 'There are no dashboard links added yet')} + > + + Dashboard links allow you to place links to other dashboards and web sites directly below the dashboard + header.{' '} + + Learn more + + + + ); } diff --git a/public/app/features/dashboard-scene/settings/variables/VariableEditorList.tsx b/public/app/features/dashboard-scene/settings/variables/VariableEditorList.tsx index b844531febb..f04e8e7b9ea 100644 --- a/public/app/features/dashboard-scene/settings/variables/VariableEditorList.tsx +++ b/public/app/features/dashboard-scene/settings/variables/VariableEditorList.tsx @@ -5,8 +5,8 @@ import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd'; import { selectors } from '@grafana/e2e-selectors'; import { reportInteraction } from '@grafana/runtime'; import { SceneVariable, SceneVariableState } from '@grafana/scenes'; -import { useStyles2, Stack, Button } from '@grafana/ui'; -import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; +import { useStyles2, Stack, Button, EmptyState, TextLink } from '@grafana/ui'; +import { t, Trans } from 'app/core/internationalization'; import { VariableEditorListRow } from './VariableEditorListRow'; @@ -98,30 +98,38 @@ export function VariableEditorList({ function EmptyVariablesList({ onAdd }: { onAdd: () => void }): ReactElement { return ( -
- - Variables enable more interactive and dynamic dashboards. Instead of hard-coding things like server - or sensor names in your metric queries you can use variables in their place. Variables are shown as - list boxes at the top of the dashboard. These drop-down lists make it easy to change the data - being displayed in your dashboard. Check out the - - Templates and variables documentation - - for more information. -

`, - }} - infoBoxTitle="What do variables do?" - onClick={(event) => { - event.preventDefault(); - onAdd(); - }} - /> -
+ + + Add variable + + } + message={t('variables.empty-state.title', 'There are no variables added yet')} + > +

+ + Variables enable more interactive and dynamic dashboards. Instead of hard-coding things like server or + sensor names in your metric queries you can use variables in their place. Variables are shown as list boxes + at the top of the dashboard. These drop-down lists make it easy to change the data being displayed in your + dashboard. + +

+ + Check out the{' '} + + Templates and variables documentation + {' '} + for more information. + +
+
); } diff --git a/public/app/features/dashboard/components/AnnotationSettings/AnnotationSettingsList.tsx b/public/app/features/dashboard/components/AnnotationSettings/AnnotationSettingsList.tsx index 10b5e49de3d..6a5579ee843 100644 --- a/public/app/features/dashboard/components/AnnotationSettings/AnnotationSettingsList.tsx +++ b/public/app/features/dashboard/components/AnnotationSettings/AnnotationSettingsList.tsx @@ -4,8 +4,8 @@ import React, { useState } from 'react'; import { arrayUtils, AnnotationQuery } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { getDataSourceSrv } from '@grafana/runtime'; -import { Button, DeleteButton, IconButton, useStyles2, VerticalGroup } from '@grafana/ui'; -import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; +import { Button, DeleteButton, EmptyState, IconButton, Stack, TextLink, useStyles2 } from '@grafana/ui'; +import { Trans, t } from 'app/core/internationalization'; import { DashboardModel } from '../../state/DashboardModel'; import { ListNewButton } from '../DashboardSettings/ListNewButton'; @@ -54,7 +54,7 @@ export const AnnotationSettingsList = ({ dashboard, onNew, onEdit }: Props) => { const dataSourceSrv = getDataSourceSrv(); return ( - + {annotations.length > 0 && (
@@ -108,25 +108,38 @@ export const AnnotationSettingsList = ({ dashboard, onNew, onEdit }: Props) => { )} {showEmptyListCTA && ( - Annotations provide a way to integrate event data into your graphs. They are visualized as vertical lines - and icons on all graph panels. When you hover over an annotation icon you can get event text & tags for - the event. You can add annotation events directly from grafana by holding CTRL or CMD + click on graph (or - drag region). These will be stored in Grafana's annotation database. -

- Checkout the - Annotations documentation - for more information.`, - }} - /> + + + Add annotation query + + } + message={t('annotations.empty-state.title', 'There are no custom annotation queries added yet')} + > + +

+ Annotations provide a way to integrate event data into your graphs. They are visualized as vertical + lines and icons on all graph panels. When you hover over an annotation icon you can get event text & + tags for the event. You can add annotation events directly from grafana by holding CTRL or CMD + click + on graph (or drag region). These will be stored in Grafana's annotation database. +

+
+ + Checkout the{' '} + + Annotations documentation + {' '} + for more information. + +
+
)} {!showEmptyListCTA && ( { New query )} - + ); }; diff --git a/public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.test.tsx index a5f0962c799..f90472ac6e8 100644 --- a/public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.test.tsx @@ -3,7 +3,6 @@ import userEvent from '@testing-library/user-event'; import React from 'react'; import { TestProvider } from 'test/helpers/TestProvider'; -import { selectors } from '@grafana/e2e-selectors'; import { locationService, setAngularLoader, setDataSourceSrv } from '@grafana/runtime'; import { mockDataSource, MockDataSourceSrv } from 'app/features/alerting/unified/mocks'; @@ -89,9 +88,7 @@ describe('AnnotationsSettings', () => { expect(screen.queryByRole('grid')).toBeInTheDocument(); expect(screen.getByRole('row', { name: /annotations & alerts \(built-in\) -- grafana --/i })).toBeInTheDocument(); - expect( - screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query')) - ).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Add annotation query' })).toBeInTheDocument(); expect(screen.queryByRole('link', { name: /annotations documentation/i })).toBeInTheDocument(); }); @@ -99,9 +96,7 @@ describe('AnnotationsSettings', () => { dashboard.annotations.list = []; setup(dashboard); - expect( - screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query')) - ).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Add annotation query' })).toBeInTheDocument(); }); test('it renders the annotation names or uid if annotation does not exist', async () => { @@ -185,7 +180,7 @@ describe('AnnotationsSettings', () => { test('Adding a new annotation', async () => { setup(dashboard); - await userEvent.click(screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query'))); + await userEvent.click(screen.getByRole('button', { name: 'Add annotation query' })); expect(locationService.getSearchObject().editIndex).toBe('1'); expect(dashboard.annotations.list.length).toBe(2); diff --git a/public/app/features/dashboard/components/DashboardSettings/LinksSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/LinksSettings.test.tsx index f4547523075..9e1b14a33ed 100644 --- a/public/app/features/dashboard/components/DashboardSettings/LinksSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/LinksSettings.test.tsx @@ -5,7 +5,6 @@ import { Provider } from 'react-redux'; import { Router } from 'react-router-dom'; import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock'; -import { selectors } from '@grafana/e2e-selectors'; import { locationService } from '@grafana/runtime'; import { GrafanaContext } from 'app/core/context/GrafanaContext'; @@ -93,9 +92,7 @@ describe('LinksSettings', () => { const linksTab = screen.getByRole('tab', { name: 'Tab Links' }); expect(linksTab).toBeInTheDocument(); expect(linksTab).toHaveAttribute('aria-selected', 'true'); - expect( - screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add dashboard link')) - ).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Add dashboard link' })).toBeInTheDocument(); expect(screen.queryByRole('table')).not.toBeInTheDocument(); }); @@ -104,9 +101,7 @@ describe('LinksSettings', () => { setup(dashboard); expect(getTableBodyRows().length).toBe(dashboard.links.length); - expect( - screen.queryByTestId(selectors.components.CallToActionCard.buttonV2('Add dashboard link')) - ).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: 'Add dashboard link' })).not.toBeInTheDocument(); }); test('it rearranges the order of dashboard links', async () => { diff --git a/public/app/features/datasources/components/DataSourcesList.tsx b/public/app/features/datasources/components/DataSourcesList.tsx index ac15fd93cb6..97c2d7f4690 100644 --- a/public/app/features/datasources/components/DataSourcesList.tsx +++ b/public/app/features/datasources/components/DataSourcesList.tsx @@ -4,10 +4,9 @@ import { useLocation } from 'react-router-dom'; import { DataSourceSettings, GrafanaTheme2 } from '@grafana/data'; import { config } from '@grafana/runtime'; -import { EmptyState, useStyles2 } from '@grafana/ui'; -import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; +import { EmptyState, LinkButton, TextLink, useStyles2 } from '@grafana/ui'; import { contextSrv } from 'app/core/core'; -import { t } from 'app/core/internationalization'; +import { Trans, t } from 'app/core/internationalization'; import { StoreState, AccessControlAction, useSelector } from 'app/types'; import { getDataSources, getDataSourcesCount, useDataSourcesRoutes, useLoadDataSources } from '../state'; @@ -67,17 +66,25 @@ export function DataSourcesListView({ if (!isLoading && dataSourcesCount === 0) { return ( - + + Add data source + + } + message={t('data-source-list.empty-state.title', 'No data sources defined')} + > + + You can also define data sources through configuration files.{' '} + + Learn more + + + ); } diff --git a/public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx b/public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx index 862f34739d9..15d0a9699fa 100644 --- a/public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx +++ b/public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx @@ -105,7 +105,7 @@ describe('LibraryPanelsSearch', () => { await getTestContext(); expect(screen.getByPlaceholderText(/search by name/i)).toBeInTheDocument(); - expect(screen.getByText(/no library panels found/i)).toBeInTheDocument(); + expect(screen.getByText(/you haven\'t created any library panels yet/i)).toBeInTheDocument(); }); describe('and user searches for library panel by name or description', () => { @@ -132,7 +132,7 @@ describe('LibraryPanelsSearch', () => { await getTestContext({ showSort: true }); expect(screen.getByPlaceholderText(/search by name/i)).toBeInTheDocument(); - expect(screen.getByText(/no library panels found/i)).toBeInTheDocument(); + expect(screen.getByText(/you haven\'t created any library panels yet/i)).toBeInTheDocument(); expect(screen.getByText(/sort \(default a–z\)/i)).toBeInTheDocument(); }); @@ -160,7 +160,7 @@ describe('LibraryPanelsSearch', () => { await getTestContext({ showPanelFilter: true }); expect(screen.getByPlaceholderText(/search by name/i)).toBeInTheDocument(); - expect(screen.getByText(/no library panels found/i)).toBeInTheDocument(); + expect(screen.getByText(/you haven\'t created any library panels yet/i)).toBeInTheDocument(); expect(screen.getByRole('combobox', { name: /panel type filter/i })).toBeInTheDocument(); }); @@ -188,7 +188,7 @@ describe('LibraryPanelsSearch', () => { await getTestContext({ showFolderFilter: true }); expect(screen.getByPlaceholderText(/search by name/i)).toBeInTheDocument(); - expect(screen.getByText(/no library panels found/i)).toBeInTheDocument(); + expect(screen.getByText(/you haven\'t created any library panels yet/i)).toBeInTheDocument(); expect(screen.getByRole('combobox', { name: /folder filter/i })).toBeInTheDocument(); }); @@ -274,7 +274,7 @@ describe('LibraryPanelsSearch', () => { const card = () => screen.getByLabelText(/plugin visualization item time series/i); - expect(screen.queryByText(/no library panels found/i)).not.toBeInTheDocument(); + expect(screen.queryByText(/you haven\'t created any library panels yet/i)).not.toBeInTheDocument(); expect(card()).toBeInTheDocument(); expect(within(card()).getByText(/library panel name/i)).toBeInTheDocument(); expect(within(card()).getByText(/library panel description/i)).toBeInTheDocument(); @@ -315,7 +315,7 @@ describe('LibraryPanelsSearch', () => { const card = () => screen.getByLabelText(/plugin visualization item time series/i); - expect(screen.queryByText(/no library panels found/i)).not.toBeInTheDocument(); + expect(screen.queryByText(/you haven\'t created any library panels yet/i)).not.toBeInTheDocument(); expect(card()).toBeInTheDocument(); expect(within(card()).getByText(/library panel name/i)).toBeInTheDocument(); expect(within(card()).getByText(/library panel description/i)).toBeInTheDocument(); diff --git a/public/app/features/library-panels/components/LibraryPanelsView/LibraryPanelsView.tsx b/public/app/features/library-panels/components/LibraryPanelsView/LibraryPanelsView.tsx index a401ba5bf72..22b7ae79026 100644 --- a/public/app/features/library-panels/components/LibraryPanelsView/LibraryPanelsView.tsx +++ b/public/app/features/library-panels/components/LibraryPanelsView/LibraryPanelsView.tsx @@ -3,8 +3,8 @@ import React, { useMemo, useReducer } from 'react'; import { useDebounce } from 'react-use'; import { GrafanaTheme2, LoadingState } from '@grafana/data'; -import { EmptyState, Pagination, Stack, useStyles2 } from '@grafana/ui'; -import { t } from 'app/core/internationalization'; +import { EmptyState, Pagination, Stack, TextLink, useStyles2 } from '@grafana/ui'; +import { Trans, t } from 'app/core/internationalization'; import { LibraryElementDTO } from '../../types'; import { LibraryPanelCard } from '../LibraryPanelCard/LibraryPanelCard'; @@ -74,6 +74,26 @@ export const LibraryPanelsView = ({ }) ); const onPageChange = (page: number) => asyncDispatch(changePage({ page })); + const hasFilter = searchString || panelFilter?.length || folderFilter?.length; + + if (!hasFilter && loadingState === LoadingState.Done && libraryPanels.length < 1) { + return ( + + + Create a library panel from any existing dashboard panel through the panel context menu.{' '} + + Learn more + + + + ); + } return ( diff --git a/public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.test.tsx b/public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.test.tsx index 6792db27a35..0e395959ad3 100644 --- a/public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.test.tsx +++ b/public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.test.tsx @@ -99,13 +99,13 @@ const renderPublicDashboardTable = async (waitForListRendering?: boolean) => { ); - waitForListRendering && (await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')[1], { timeout: 3000 })); + waitForListRendering && (await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')[0], { timeout: 3000 })); }; describe('Show table', () => { it('renders loader spinner while loading', async () => { await renderPublicDashboardTable(); - const spinner = screen.getAllByTestId('Spinner')[1]; + const spinner = screen.getAllByTestId('Spinner')[0]; expect(spinner).toBeInTheDocument(); await waitForElementToBeRemoved(spinner); diff --git a/public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.tsx b/public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.tsx index 5486f77cf75..c0a6d4e4d1f 100644 --- a/public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.tsx +++ b/public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.tsx @@ -2,7 +2,7 @@ import { css } from '@emotion/css'; import React, { useMemo, useState } from 'react'; import { useMedia } from 'react-use'; -import { GrafanaTheme2 } from '@grafana/data/src'; +import { GrafanaTheme2 } from '@grafana/data'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; import { reportInteraction } from '@grafana/runtime'; import { @@ -16,7 +16,9 @@ import { Switch, Pagination, HorizontalGroup, -} from '@grafana/ui/src'; + EmptyState, + TextLink, +} from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; import { Trans, t } from 'app/core/internationalization'; import { contextSrv } from 'app/core/services/context_srv'; @@ -141,28 +143,50 @@ export const PublicDashboardListTable = () => { const [page, setPage] = useState(1); const styles = useStyles2(getStyles); - const { data: paginatedPublicDashboards, isLoading, isFetching, isError } = useListPublicDashboardsQuery(page); + const { data: paginatedPublicDashboards, isLoading, isError } = useListPublicDashboardsQuery(page); return ( - }> + {!isLoading && !isError && !!paginatedPublicDashboards && (
-
    - {paginatedPublicDashboards.publicDashboards.map((pd: PublicDashboardListResponse) => ( -
  • - -
  • - ))} -
- - - + {paginatedPublicDashboards.publicDashboards.length === 0 ? ( + + + Create a public dashboard from any existing dashboard through the Share modal.{' '} + + Learn more + + + + ) : ( + <> +
    + {paginatedPublicDashboards.publicDashboards.map((pd: PublicDashboardListResponse) => ( +
  • + +
  • + ))} +
+ + + + + )}
)}
diff --git a/public/app/features/manage-dashboards/components/SnapshotListTable.tsx b/public/app/features/manage-dashboards/components/SnapshotListTable.tsx index 37e1d7d03c3..aeac45764c1 100644 --- a/public/app/features/manage-dashboards/components/SnapshotListTable.tsx +++ b/public/app/features/manage-dashboards/components/SnapshotListTable.tsx @@ -2,13 +2,13 @@ import React, { useState, useCallback } from 'react'; import useAsync from 'react-use/lib/useAsync'; import { config } from '@grafana/runtime'; -import { ConfirmModal } from '@grafana/ui'; -import { Trans } from 'app/core/internationalization'; +import { ConfirmModal, EmptyState, TextLink } from '@grafana/ui'; +import { Trans, t } from 'app/core/internationalization'; import { getDashboardSnapshotSrv, Snapshot } from 'app/features/dashboard/services/SnapshotSrv'; import { SnapshotListTableRow } from './SnapshotListTableRow'; -export function getSnapshots() { +export async function getSnapshots() { return getDashboardSnapshotSrv() .getSnapshots() .then((result: Snapshot[]) => { @@ -42,6 +42,25 @@ export const SnapshotListTable = () => { [snapshots] ); + if (!isFetching && snapshots.length === 0) { + return ( + + + You can create a snapshot of any dashboard through the Share modal.{' '} + + Learn more + + + + ); + } + return (
diff --git a/public/app/features/playlist/PlaylistPage.tsx b/public/app/features/playlist/PlaylistPage.tsx index bebb5f3c906..f8cb99c6c77 100644 --- a/public/app/features/playlist/PlaylistPage.tsx +++ b/public/app/features/playlist/PlaylistPage.tsx @@ -1,8 +1,7 @@ import React, { useMemo, useState } from 'react'; import { useAsync } from 'react-use'; -import { ConfirmModal, EmptyState, LinkButton } from '@grafana/ui'; -import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; +import { ConfirmModal, EmptyState, LinkButton, TextLink } from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; import PageActionBar from 'app/core/components/PageActionBar/PageActionBar'; import { Trans, t } from 'app/core/internationalization'; @@ -35,20 +34,6 @@ export const PlaylistPage = () => { }); }; - const emptyListBanner = ( - - ); - const showSearch = allPlaylists.loading || playlists.length > 0 || searchQuery.length > 0; return ( @@ -78,7 +63,24 @@ export const PlaylistPage = () => { setPlaylistToDelete={setPlaylistToDelete} /> )} - {!showSearch && emptyListBanner} + {!showSearch && ( + + Create playlist + + } + message={t('playlist-page.empty.title', 'There are no playlists created yet')} + > + + You can use playlists to cycle dashboards on TVs without user control.{' '} + + Learn more + + + + )} {playlistToDelete && ( )} {!isLoading && noServiceAccountsCreated && ( - <> - - + + Add service account + + } + message={t('service-accounts.empty-state.title', "You haven't created any service accounts yet")} + > + + Remember, you can provide specific permissions for API access to other applications + + )} {(isLoading || serviceAccounts.length !== 0) && ( diff --git a/public/app/features/teams/TeamList.tsx b/public/app/features/teams/TeamList.tsx index 9830d3b7248..4940ec5bb60 100644 --- a/public/app/features/teams/TeamList.tsx +++ b/public/app/features/teams/TeamList.tsx @@ -19,10 +19,9 @@ import { TextLink, useStyles2, } from '@grafana/ui'; -import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import { Page } from 'app/core/components/Page/Page'; import { fetchRoleOptions } from 'app/core/components/RolePicker/api'; -import { t } from 'app/core/internationalization'; +import { Trans, t } from 'app/core/internationalization'; import { contextSrv } from 'app/core/services/context_srv'; import { AccessControlAction, Role, StoreState, Team } from 'app/types'; @@ -208,24 +207,31 @@ export const TeamList = ({ - New Team - + !noTeams ? ( + + New Team + + ) : undefined } > {noTeams ? ( - + + New team + + } + message={t('teams.empty-state.title', "You haven't created any teams yet")} + > + + Assign folder and dashboard permissions to teams instead of users to ease administration.{' '} + + Learn more + + + ) : ( <>
diff --git a/public/app/features/variables/editor/VariableEditorList.tsx b/public/app/features/variables/editor/VariableEditorList.tsx index 32720af0ab6..7dc62f8e86d 100644 --- a/public/app/features/variables/editor/VariableEditorList.tsx +++ b/public/app/features/variables/editor/VariableEditorList.tsx @@ -5,8 +5,8 @@ import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd'; import { TypedVariableModel } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { reportInteraction } from '@grafana/runtime'; -import { Button, useStyles2, Stack } from '@grafana/ui'; -import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; +import { Button, useStyles2, Stack, EmptyState, TextLink } from '@grafana/ui'; +import { t, Trans } from 'app/core/internationalization'; import { VariablesDependenciesButton } from '../inspect/VariablesDependenciesButton'; import { UsagesToNetwork, VariableUsageTree } from '../inspect/utils'; @@ -107,30 +107,38 @@ export function VariableEditorList({ function EmptyVariablesList({ onAdd }: { onAdd: () => void }): ReactElement { return ( -
- - Variables enable more interactive and dynamic dashboards. Instead of hard-coding things like server - or sensor names in your metric queries you can use variables in their place. Variables are shown as - list boxes at the top of the dashboard. These drop-down lists make it easy to change the data - being displayed in your dashboard. Check out the - - Templates and variables documentation - - for more information. -

`, - }} - infoBoxTitle="What do variables do?" - onClick={(event) => { - event.preventDefault(); - onAdd(); - }} - /> -
+ + + Add variable + + } + message={t('variables.empty-state.title', 'There are no variables added yet')} + > +

+ + Variables enable more interactive and dynamic dashboards. Instead of hard-coding things like server or + sensor names in your metric queries you can use variables in their place. Variables are shown as list boxes + at the top of the dashboard. These drop-down lists make it easy to change the data being displayed in your + dashboard. + +

+ + Check out the{' '} + + Templates and variables documentation + {' '} + for more information. + +
+
); } diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 1e02e4d8c1d..b783638694f 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -25,6 +25,14 @@ "user": "User" } }, + "annotations": { + "empty-state": { + "button-title": "Add annotation query", + "info-box-content": "<0>Annotations provide a way to integrate event data into your graphs. They are visualized as vertical lines and icons on all graph panels. When you hover over an annotation icon you can get event text & tags for the event. You can add annotation events directly from grafana by holding CTRL or CMD + click on graph (or drag region). These will be stored in Grafana's annotation database.", + "info-box-content-2": "Checkout the <2>Annotations documentation for more information.", + "title": "There are no custom annotation queries added yet" + } + }, "api-keys": { "empty-state": { "message": "No API keys found" @@ -72,6 +80,12 @@ "select-checkbox": "Select", "tags-column": "Tags" }, + "empty-state": { + "button-title": "Create dashboard", + "pro-tip": "Add/move dashboards to your folder at <2>Browse dashboards", + "title": "You haven't created any dashboards yet", + "title-folder": "This folder doesn't have any dashboards yet" + }, "folder-actions-button": { "delete": "Delete", "folder-actions": "Folder actions", @@ -159,6 +173,11 @@ "sub-text": "<0>Define text that will describe the correlation.", "title": "Define correlation label (Step 1 of 3)" }, + "empty-state": { + "button-title": "Add correlation", + "pro-tip": "You can also define correlations via datasource provisioning", + "title": "You haven't defined any correlations yet" + }, "list": { "delete": "delete correlation", "label": "Label", @@ -355,6 +374,13 @@ "validation-required": "Need a dashboard JSON model" } }, + "dashboard-links": { + "empty-state": { + "button-title": "Add dashboard link", + "info-box-content": "Dashboard links allow you to place links to other dashboards and web sites directly below the dashboard header. <2>Learn more", + "title": "There are no dashboard links added yet" + } + }, "dashboard-settings": { "annotations": { "title": "Annotations" @@ -405,6 +431,13 @@ "title": "Versions" } }, + "data-source-list": { + "empty-state": { + "button-title": "Add data source", + "pro-tip": "You can also define data sources through configuration files. <2>Learn more", + "title": "No data sources defined" + } + }, "data-source-picker": { "add-new-data-source": "Configure a new data source", "built-in-list": { @@ -657,6 +690,10 @@ }, "add-widget": { "title": "Add panel from panel library" + }, + "empty-state": { + "message": "You haven't created any library panels yet", + "more-info": "Create a library panel from any existing dashboard panel through the panel context menu. <2>Learn more" } }, "library-panels": { @@ -1230,9 +1267,8 @@ "confirm-text": "Delete" }, "empty": { - "button": "Create Playlist", - "pro-tip": "You can use playlists to cycle dashboards on TVs without user control", - "pro-tip-link-title": "Learn more", + "button": "Create playlist", + "pro-tip": "You can use playlists to cycle dashboards on TVs without user control. <2>Learn more", "title": "There are no playlists created yet" } }, @@ -1346,6 +1382,10 @@ "orphaned-title": "<0>Orphaned public dashboard", "orphaned-tooltip": "The linked dashboard has already been deleted" }, + "empty-state": { + "message": "You haven't created any public dashboards yet", + "more-info": "Create a public dashboard from any existing dashboard through the <1>Share modal. <4>Learn more" + }, "toggle": { "pause-sharing-toggle-text": "Pause sharing" } @@ -1450,7 +1490,10 @@ }, "service-accounts": { "empty-state": { - "message": "No services accounts found" + "button-title": "Add service account", + "message": "No services accounts found", + "more-info": "Remember, you can provide specific permissions for API access to other applications", + "title": "You haven't created any service accounts yet" } }, "share-modal": { @@ -1570,7 +1613,17 @@ }, "title": "Preferences" }, + "silences": { + "empty-state": { + "button-title": "Create silence", + "title": "You haven't created any silences yet" + } + }, "snapshot": { + "empty-state": { + "message": "You haven't created any snapshots yet", + "more-info": "You can create a snapshot of any dashboard through the <1>Share modal. <4>Learn more" + }, "external-badge": "External", "name-column-header": "Name", "url-column-header": "Snapshot url", @@ -1583,7 +1636,10 @@ }, "teams": { "empty-state": { - "message": "No teams found" + "button-title": "New team", + "message": "No teams found", + "pro-tip": "Assign folder and dashboard permissions to teams instead of users to ease administration. <2>Learn more", + "title": "You haven't created any teams yet" } }, "time-picker": { @@ -1705,5 +1761,13 @@ "textbox": { "placeholder": "Enter variable value" } + }, + "variables": { + "empty-state": { + "button-title": "Add variable", + "info-box-content": "Variables enable more interactive and dynamic dashboards. Instead of hard-coding things like server or sensor names in your metric queries you can use variables in their place. Variables are shown as list boxes at the top of the dashboard. These drop-down lists make it easy to change the data being displayed in your dashboard.", + "info-box-content-2": "Check out the <2>Templates and variables documentation for more information.", + "title": "There are no variables added yet" + } } } diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index 07cb64d01df..9fa2685c1ed 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -25,6 +25,14 @@ "user": "Ůşęř" } }, + "annotations": { + "empty-state": { + "button-title": "Åđđ äʼnʼnőŧäŧįőʼn qūęřy", + "info-box-content": "<0>Åʼnʼnőŧäŧįőʼnş přővįđę ä ŵäy ŧő įʼnŧęģřäŧę ęvęʼnŧ đäŧä įʼnŧő yőūř ģřäpĥş. Ŧĥęy äřę vįşūäľįžęđ äş vęřŧįčäľ ľįʼnęş äʼnđ įčőʼnş őʼn äľľ ģřäpĥ päʼnęľş. Ŵĥęʼn yőū ĥővęř ővęř äʼn äʼnʼnőŧäŧįőʼn įčőʼn yőū čäʼn ģęŧ ęvęʼnŧ ŧęχŧ & ŧäģş ƒőř ŧĥę ęvęʼnŧ. Ÿőū čäʼn äđđ äʼnʼnőŧäŧįőʼn ęvęʼnŧş đįřęčŧľy ƒřőm ģřäƒäʼnä þy ĥőľđįʼnģ CŦŖĿ őř CMĐ + čľįčĸ őʼn ģřäpĥ (őř đřäģ řęģįőʼn). Ŧĥęşę ŵįľľ þę şŧőřęđ įʼn Ğřäƒäʼnä'ş äʼnʼnőŧäŧįőʼn đäŧäþäşę.", + "info-box-content-2": "Cĥęčĸőūŧ ŧĥę <2>Åʼnʼnőŧäŧįőʼnş đőčūmęʼnŧäŧįőʼn ƒőř mőřę įʼnƒőřmäŧįőʼn.", + "title": "Ŧĥęřę äřę ʼnő čūşŧőm äʼnʼnőŧäŧįőʼn qūęřįęş äđđęđ yęŧ" + } + }, "api-keys": { "empty-state": { "message": "Ńő ÅPĨ ĸęyş ƒőūʼnđ" @@ -72,6 +80,12 @@ "select-checkbox": "Ŝęľęčŧ", "tags-column": "Ŧäģş" }, + "empty-state": { + "button-title": "Cřęäŧę đäşĥþőäřđ", + "pro-tip": "Åđđ/mővę đäşĥþőäřđş ŧő yőūř ƒőľđęř äŧ <2>ßřőŵşę đäşĥþőäřđş", + "title": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny đäşĥþőäřđş yęŧ", + "title-folder": "Ŧĥįş ƒőľđęř đőęşʼn'ŧ ĥävę äʼny đäşĥþőäřđş yęŧ" + }, "folder-actions-button": { "delete": "Đęľęŧę", "folder-actions": "Főľđęř äčŧįőʼnş", @@ -159,6 +173,11 @@ "sub-text": "<0>Đęƒįʼnę ŧęχŧ ŧĥäŧ ŵįľľ đęşčřįþę ŧĥę čőřřęľäŧįőʼn.", "title": "Đęƒįʼnę čőřřęľäŧįőʼn ľäþęľ (Ŝŧęp 1 őƒ 3)" }, + "empty-state": { + "button-title": "Åđđ čőřřęľäŧįőʼn", + "pro-tip": "Ÿőū čäʼn äľşő đęƒįʼnę čőřřęľäŧįőʼnş vįä đäŧäşőūřčę přővįşįőʼnįʼnģ", + "title": "Ÿőū ĥävęʼn'ŧ đęƒįʼnęđ äʼny čőřřęľäŧįőʼnş yęŧ" + }, "list": { "delete": "đęľęŧę čőřřęľäŧįőʼn", "label": "Ŀäþęľ", @@ -355,6 +374,13 @@ "validation-required": "Ńęęđ ä đäşĥþőäřđ ĴŜØŃ mőđęľ" } }, + "dashboard-links": { + "empty-state": { + "button-title": "Åđđ đäşĥþőäřđ ľįʼnĸ", + "info-box-content": "Đäşĥþőäřđ ľįʼnĸş äľľőŵ yőū ŧő pľäčę ľįʼnĸş ŧő őŧĥęř đäşĥþőäřđş äʼnđ ŵęþ şįŧęş đįřęčŧľy þęľőŵ ŧĥę đäşĥþőäřđ ĥęäđęř. <2>Ŀęäřʼn mőřę", + "title": "Ŧĥęřę äřę ʼnő đäşĥþőäřđ ľįʼnĸş äđđęđ yęŧ" + } + }, "dashboard-settings": { "annotations": { "title": "Åʼnʼnőŧäŧįőʼnş" @@ -405,6 +431,13 @@ "title": "Vęřşįőʼnş" } }, + "data-source-list": { + "empty-state": { + "button-title": "Åđđ đäŧä şőūřčę", + "pro-tip": "Ÿőū čäʼn äľşő đęƒįʼnę đäŧä şőūřčęş ŧĥřőūģĥ čőʼnƒįģūřäŧįőʼn ƒįľęş. <2>Ŀęäřʼn mőřę", + "title": "Ńő đäŧä şőūřčęş đęƒįʼnęđ" + } + }, "data-source-picker": { "add-new-data-source": "Cőʼnƒįģūřę ä ʼnęŵ đäŧä şőūřčę", "built-in-list": { @@ -657,6 +690,10 @@ }, "add-widget": { "title": "Åđđ päʼnęľ ƒřőm päʼnęľ ľįþřäřy" + }, + "empty-state": { + "message": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny ľįþřäřy päʼnęľş yęŧ", + "more-info": "Cřęäŧę ä ľįþřäřy päʼnęľ ƒřőm äʼny ęχįşŧįʼnģ đäşĥþőäřđ päʼnęľ ŧĥřőūģĥ ŧĥę päʼnęľ čőʼnŧęχŧ męʼnū. <2>Ŀęäřʼn mőřę" } }, "library-panels": { @@ -1230,9 +1267,8 @@ "confirm-text": "Đęľęŧę" }, "empty": { - "button": "Cřęäŧę Pľäyľįşŧ", - "pro-tip": "Ÿőū čäʼn ūşę pľäyľįşŧş ŧő čyčľę đäşĥþőäřđş őʼn ŦVş ŵįŧĥőūŧ ūşęř čőʼnŧřőľ", - "pro-tip-link-title": "Ŀęäřʼn mőřę", + "button": "Cřęäŧę pľäyľįşŧ", + "pro-tip": "Ÿőū čäʼn ūşę pľäyľįşŧş ŧő čyčľę đäşĥþőäřđş őʼn ŦVş ŵįŧĥőūŧ ūşęř čőʼnŧřőľ. <2>Ŀęäřʼn mőřę", "title": "Ŧĥęřę äřę ʼnő pľäyľįşŧş čřęäŧęđ yęŧ" } }, @@ -1346,6 +1382,10 @@ "orphaned-title": "<0>Øřpĥäʼnęđ pūþľįč đäşĥþőäřđ", "orphaned-tooltip": "Ŧĥę ľįʼnĸęđ đäşĥþőäřđ ĥäş äľřęäđy þęęʼn đęľęŧęđ" }, + "empty-state": { + "message": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny pūþľįč đäşĥþőäřđş yęŧ", + "more-info": "Cřęäŧę ä pūþľįč đäşĥþőäřđ ƒřőm äʼny ęχįşŧįʼnģ đäşĥþőäřđ ŧĥřőūģĥ ŧĥę <1>Ŝĥäřę mőđäľ. <4>Ŀęäřʼn mőřę" + }, "toggle": { "pause-sharing-toggle-text": "Päūşę şĥäřįʼnģ" } @@ -1450,7 +1490,10 @@ }, "service-accounts": { "empty-state": { - "message": "Ńő şęřvįčęş äččőūʼnŧş ƒőūʼnđ" + "button-title": "Åđđ şęřvįčę äččőūʼnŧ", + "message": "Ńő şęřvįčęş äččőūʼnŧş ƒőūʼnđ", + "more-info": "Ŗęmęmþęř, yőū čäʼn přővįđę şpęčįƒįč pęřmįşşįőʼnş ƒőř ÅPĨ äččęşş ŧő őŧĥęř äppľįčäŧįőʼnş", + "title": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny şęřvįčę äččőūʼnŧş yęŧ" } }, "share-modal": { @@ -1570,7 +1613,17 @@ }, "title": "Přęƒęřęʼnčęş" }, + "silences": { + "empty-state": { + "button-title": "Cřęäŧę şįľęʼnčę", + "title": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny şįľęʼnčęş yęŧ" + } + }, "snapshot": { + "empty-state": { + "message": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny şʼnäpşĥőŧş yęŧ", + "more-info": "Ÿőū čäʼn čřęäŧę ä şʼnäpşĥőŧ őƒ äʼny đäşĥþőäřđ ŧĥřőūģĥ ŧĥę <1>Ŝĥäřę mőđäľ. <4>Ŀęäřʼn mőřę" + }, "external-badge": "Ēχŧęřʼnäľ", "name-column-header": "Ńämę", "url-column-header": "Ŝʼnäpşĥőŧ ūřľ", @@ -1583,7 +1636,10 @@ }, "teams": { "empty-state": { - "message": "Ńő ŧęämş ƒőūʼnđ" + "button-title": "Ńęŵ ŧęäm", + "message": "Ńő ŧęämş ƒőūʼnđ", + "pro-tip": "Åşşįģʼn ƒőľđęř äʼnđ đäşĥþőäřđ pęřmįşşįőʼnş ŧő ŧęämş įʼnşŧęäđ őƒ ūşęřş ŧő ęäşę äđmįʼnįşŧřäŧįőʼn. <2>Ŀęäřʼn mőřę", + "title": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny ŧęämş yęŧ" } }, "time-picker": { @@ -1705,5 +1761,13 @@ "textbox": { "placeholder": "Ēʼnŧęř väřįäþľę väľūę" } + }, + "variables": { + "empty-state": { + "button-title": "Åđđ väřįäþľę", + "info-box-content": "Väřįäþľęş ęʼnäþľę mőřę įʼnŧęřäčŧįvę äʼnđ đyʼnämįč đäşĥþőäřđş. Ĩʼnşŧęäđ őƒ ĥäřđ-čőđįʼnģ ŧĥįʼnģş ľįĸę şęřvęř őř şęʼnşőř ʼnämęş įʼn yőūř męŧřįč qūęřįęş yőū čäʼn ūşę väřįäþľęş įʼn ŧĥęįř pľäčę. Väřįäþľęş äřę şĥőŵʼn äş ľįşŧ þőχęş äŧ ŧĥę ŧőp őƒ ŧĥę đäşĥþőäřđ. Ŧĥęşę đřőp-đőŵʼn ľįşŧş mäĸę įŧ ęäşy ŧő čĥäʼnģę ŧĥę đäŧä þęįʼnģ đįşpľäyęđ įʼn yőūř đäşĥþőäřđ.", + "info-box-content-2": "Cĥęčĸ őūŧ ŧĥę <2>Ŧęmpľäŧęş äʼnđ väřįäþľęş đőčūmęʼnŧäŧįőʼn ƒőř mőřę įʼnƒőřmäŧįőʼn.", + "title": "Ŧĥęřę äřę ʼnő väřįäþľęş äđđęđ yęŧ" + } } }