diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/ImpactfulMetrics/JSErrors.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/ImpactfulMetrics/JSErrors.tsx index a7b1d99072d26..c8956c091d267 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/ImpactfulMetrics/JSErrors.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/ImpactfulMetrics/JSErrors.tsx @@ -16,9 +16,10 @@ import { EuiToolTip, } from '@elastic/eui'; import numeral from '@elastic/numeral'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; -import { useFetcher } from '../../../../hooks/use_fetcher'; +import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; import { I18LABELS } from '../translations'; import { CsmSharedContext } from '../CsmSharedContext'; import { ErrorDetailLink } from '../../../shared/Links/apm/ErrorDetailLink'; @@ -125,14 +126,21 @@ export function JSErrors() { ) } description={I18LABELS.totalErrors} - isLoading={status !== 'success'} + isLoading={status === FETCH_STATUS.LOADING} /> ({ status === FETCH_STATUS.LOADING ? loadingText : noDataText } loading={status === FETCH_STATUS.LOADING} + error={status === FETCH_STATUS.FAILURE ? errorMessage : ''} columns={columns} rowProps={(term) => { return { @@ -121,3 +122,8 @@ const noDataText = i18n.translate( 'xpack.apm.correlations.correlationsTable.noDataText', { defaultMessage: 'No data' } ); + +const errorMessage = i18n.translate( + 'xpack.apm.correlations.correlationsTable.errorMessage', + { defaultMessage: 'Failed to fetch' } +); diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx index 2cdb808622854..0b505c4f5ade8 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx @@ -5,7 +5,12 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiEmptyPrompt, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useEffect } from 'react'; import uuid from 'uuid'; @@ -20,7 +25,6 @@ import { useTimeRange } from '../../../hooks/use_time_range'; import { useUpgradeAssistantHref } from '../../shared/Links/kibana'; import { SearchBar } from '../../shared/search_bar'; import { getTimeRangeComparison } from '../../shared/time_comparison/get_time_range_comparison'; -import { NoServicesMessage } from './no_services_message'; import { ServiceList } from './service_list'; import { MLCallout } from './service_list/MLCallout'; @@ -179,6 +183,21 @@ export function ServiceInventory() { canCreateJob && !userHasDismissedCallout; + const isLoading = mainStatisticsStatus === FETCH_STATUS.LOADING; + const isFailure = mainStatisticsStatus === FETCH_STATUS.FAILURE; + const noItemsMessage = ( + + {i18n.translate('xpack.apm.servicesTable.notFoundLabel', { + defaultMessage: 'No services found', + })} + + } + titleSize="s" + /> + ); + return ( <> @@ -190,10 +209,11 @@ export function ServiceInventory() { )} } + noItemsMessage={noItemsMessage} /> diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/no_services_message.test.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/no_services_message.test.tsx deleted file mode 100644 index 3a3ddcc558679..0000000000000 --- a/x-pack/plugins/apm/public/components/app/service_inventory/no_services_message.test.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { render } from '@testing-library/react'; -import React, { ReactNode } from 'react'; -import { MockApmPluginContextWrapper } from '../../../context/apm_plugin/mock_apm_plugin_context'; -import { FETCH_STATUS } from '../../../hooks/use_fetcher'; -import { NoServicesMessage } from './no_services_message'; - -function Wrapper({ children }: { children?: ReactNode }) { - return {children}; -} - -describe('NoServicesMessage', () => { - Object.values(FETCH_STATUS).forEach((status) => { - [true, false].forEach((historicalDataFound) => { - describe(`when status is ${status}`, () => { - describe(`when historicalDataFound is ${historicalDataFound}`, () => { - it('renders', () => { - expect(() => - render(, { - wrapper: Wrapper, - }) - ).not.toThrowError(); - }); - }); - }); - }); - }); -}); diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/no_services_message.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/no_services_message.tsx deleted file mode 100644 index 872359262cf02..0000000000000 --- a/x-pack/plugins/apm/public/components/app/service_inventory/no_services_message.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiEmptyPrompt } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { FETCH_STATUS } from '../../../hooks/use_fetcher'; -import { ErrorStatePrompt } from '../../shared/ErrorStatePrompt'; - -interface Props { - status: FETCH_STATUS | undefined; -} - -export function NoServicesMessage({ status }: Props) { - if (status === FETCH_STATUS.LOADING) { - return null; - } - - if (status === FETCH_STATUS.FAILURE) { - return ; - } - - return ( - - {i18n.translate('xpack.apm.servicesTable.notFoundLabel', { - defaultMessage: 'No services found', - })} - - } - titleSize="s" - /> - ); -} diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx index e085c5794f80a..17dfee35e221b 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx @@ -214,6 +214,7 @@ interface Props { comparisonData?: ServicesDetailedStatisticsAPIResponse; noItemsMessage?: React.ReactNode; isLoading: boolean; + isFailure?: boolean; } export function ServiceList({ @@ -221,6 +222,7 @@ export function ServiceList({ noItemsMessage, comparisonData, isLoading, + isFailure, }: Props) { const breakpoints = useBreakpoints(); const displayHealthStatus = items.some((item) => 'healthStatus' in item); @@ -296,6 +298,7 @@ export function ServiceList({ - - + - { - setTableOptions({ - pageIndex: newTableOptions.page?.index ?? 0, - sort: newTableOptions.sort - ? { - field: newTableOptions.sort.field as SortField, - direction: newTableOptions.sort.direction, - } - : DEFAULT_SORT, - }); - }} - sorting={{ - enableAllColumns: true, - sort, - }} - /> - - + noItemsMessage={ + status === FETCH_STATUS.LOADING + ? i18n.translate( + 'xpack.apm.serviceOverview.errorsTable.loading', + { defaultMessage: 'Loading...' } + ) + : i18n.translate( + 'xpack.apm.serviceOverview.errorsTable.noResults', + { defaultMessage: 'No errors found' } + ) + } + columns={columns} + items={items} + pagination={{ + pageIndex, + pageSize: PAGE_SIZE, + totalItemCount: totalItems, + pageSizeOptions: [PAGE_SIZE], + hidePerPageOptions: true, + }} + loading={status === FETCH_STATUS.LOADING} + onChange={(newTableOptions: { + page?: { + index: number; + }; + sort?: { field: string; direction: SortDirection }; + }) => { + setTableOptions({ + pageIndex: newTableOptions.page?.index ?? 0, + sort: newTableOptions.sort + ? { + field: newTableOptions.sort.field as SortField, + direction: newTableOptions.sort.direction, + } + : DEFAULT_SORT, + }); + }} + sorting={{ + enableAllColumns: true, + sort, + }} + /> + ); diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx index 9312a3c68a1ec..1df6803fdbbb1 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx @@ -17,7 +17,6 @@ import { useApmServiceContext } from '../../../../context/apm_service/use_apm_se import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; import { APIReturnType } from '../../../../services/rest/createCallApmApi'; -import { TableFetchWrapper } from '../../../shared/table_fetch_wrapper'; import { PAGE_SIZE, SortDirection, @@ -156,33 +155,39 @@ export function ServiceOverviewInstancesTable({ - - - - - + + + ); diff --git a/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx b/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx index 5286b821dd23f..63cd27cba41e8 100644 --- a/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx @@ -65,6 +65,7 @@ export function TraceOverview() { ); diff --git a/x-pack/plugins/apm/public/components/app/trace_overview/trace_list.tsx b/x-pack/plugins/apm/public/components/app/trace_overview/trace_list.tsx index 765a4b1aeae8c..7c4e0ffca1f58 100644 --- a/x-pack/plugins/apm/public/components/app/trace_overview/trace_list.tsx +++ b/x-pack/plugins/apm/public/components/app/trace_overview/trace_list.tsx @@ -30,6 +30,7 @@ const StyledTransactionLink = euiStyled(TransactionDetailLink)` interface Props { items: TraceGroup[]; isLoading: boolean; + isFailure: boolean; } const traceListColumns: Array> = [ @@ -124,10 +125,11 @@ const noItemsMessage = ( /> ); -export function TraceList({ items = [], isLoading }: Props) { +export function TraceList({ items = [], isLoading, isFailure }: Props) { return ( - - - - - + + + ); diff --git a/x-pack/plugins/apm/public/components/shared/managed_table/__snapshots__/managed_table.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/managed_table/__snapshots__/managed_table.test.tsx.snap index 1e01c00543949..e4674b3add880 100644 --- a/x-pack/plugins/apm/public/components/shared/managed_table/__snapshots__/managed_table.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/shared/managed_table/__snapshots__/managed_table.test.tsx.snap @@ -17,6 +17,7 @@ exports[`ManagedTable should render a page-full of items, with defaults 1`] = ` }, ] } + error="" items={ Array [ Object { @@ -74,6 +75,7 @@ exports[`ManagedTable should render when specifying initial values 1`] = ` }, ] } + error="" items={ Array [ Object { diff --git a/x-pack/plugins/apm/public/components/shared/managed_table/index.tsx b/x-pack/plugins/apm/public/components/shared/managed_table/index.tsx index f7009956bcf84..54f0aebbe818c 100644 --- a/x-pack/plugins/apm/public/components/shared/managed_table/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/managed_table/index.tsx @@ -43,6 +43,7 @@ interface Props { ) => T[]; pagination?: boolean; isLoading?: boolean; + error?: boolean; } function defaultSortFn( @@ -68,6 +69,7 @@ function UnoptimizedManagedTable(props: Props) { sortFn = defaultSortFn, pagination = true, isLoading = false, + error = false, } = props; const { @@ -138,6 +140,13 @@ function UnoptimizedManagedTable(props: Props) { return ( >} // EuiBasicTableColumn is stricter than ITableColumn diff --git a/x-pack/plugins/apm/public/components/shared/table_fetch_wrapper/index.tsx b/x-pack/plugins/apm/public/components/shared/table_fetch_wrapper/index.tsx deleted file mode 100644 index 8a87ede9b3daa..0000000000000 --- a/x-pack/plugins/apm/public/components/shared/table_fetch_wrapper/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { ReactNode } from 'react'; -import { FETCH_STATUS } from '../../../hooks/use_fetcher'; -import { ErrorStatePrompt } from '../ErrorStatePrompt'; - -export function TableFetchWrapper({ - status, - children, -}: { - status: FETCH_STATUS; - children: ReactNode; -}) { - if (status === FETCH_STATUS.FAILURE) { - return ; - } - - return <>{children}; -} diff --git a/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx b/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx index 08fc9e54c1444..60612b581e1d4 100644 --- a/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx @@ -23,7 +23,6 @@ import { useApmServiceContext } from '../../../context/apm_service/use_apm_servi import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { TransactionOverviewLink } from '../Links/apm/transaction_overview_link'; -import { TableFetchWrapper } from '../table_fetch_wrapper'; import { getTimeRangeComparison } from '../time_comparison/get_time_range_comparison'; import { OverviewTableContainer } from '../overview_table_container'; import { getColumns } from './get_columns'; @@ -302,47 +301,52 @@ export function TransactionsTable({ )} - - + - { - setTableOptions({ - pageIndex: newTableOptions.page?.index ?? 0, - sort: newTableOptions.sort - ? { - field: newTableOptions.sort.field as SortField, - direction: newTableOptions.sort.direction, - } - : DEFAULT_SORT, - }); - }} - /> - - + loading={isLoading} + error={ + status === FETCH_STATUS.FAILURE + ? i18n.translate('xpack.apm.transactionsTable.errorMessage', { + defaultMessage: 'Failed to fetch', + }) + : '' + } + items={transactionGroups} + columns={columns} + pagination={pagination} + sorting={{ sort: { field, direction } }} + onChange={(newTableOptions: { + page?: { + index: number; + }; + sort?: { field: string; direction: SortDirection }; + }) => { + setTableOptions({ + pageIndex: newTableOptions.page?.index ?? 0, + sort: newTableOptions.sort + ? { + field: newTableOptions.sort.field as SortField, + direction: newTableOptions.sort.direction, + } + : DEFAULT_SORT, + }); + }} + /> +