Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,64 @@ describe('MetricsExperienceGrid', () => {
expect(getByTestId('metricsExperienceToolbarFullScreen')).toBeInTheDocument();
});

it('renders only the METRICS_INFO error state when fetch fails', () => {
useFetchMetricsDataMock.mockReturnValue({
metricItems: [],
allDimensions: [],
loading: false,
error: new Error('METRICS_INFO failed'),
});
useMetricFieldsFilterMock.mockReturnValue({ filteredMetricItems: [] });

const { getByTestId, queryByTestId } = render(<MetricsExperienceGrid {...defaultProps} />, {
wrapper: IntlProvider,
});

expect(getByTestId('metricsInfoError')).toBeInTheDocument();
expect(getByTestId('metricsInfoErrorTitle')).toHaveTextContent('Unable to load visualization');
expect(getByTestId('metricsInfoErrorDescription')).toHaveTextContent(
'trouble retrieving the information needed for this visualization'
);
expect(queryByTestId('toggleActions')).not.toBeInTheDocument();
});

it('does not render the METRICS_INFO error state for AbortError (shows chart grid instead)', () => {
const abortError = new Error('Aborted');
abortError.name = 'AbortError';

useFetchMetricsDataMock.mockReturnValue({
metricItems: [],
allDimensions: [],
loading: false,
error: abortError,
});
useMetricFieldsFilterMock.mockReturnValue({ filteredMetricItems: [] });

const { queryByTestId, getByTestId } = render(<MetricsExperienceGrid {...defaultProps} />, {
wrapper: IntlProvider,
});

expect(queryByTestId('metricsInfoError')).not.toBeInTheDocument();
expect(getByTestId('toggleActions')).toBeInTheDocument();
});

it('shows loading empty state instead of METRICS_INFO error while fetch is in progress', () => {
useFetchMetricsDataMock.mockReturnValue({
metricItems: [],
allDimensions: [],
loading: true,
error: new Error('stale error while refetching'),
});
useMetricFieldsFilterMock.mockReturnValue({ filteredMetricItems: [] });

const { queryByTestId, getByTestId } = render(<MetricsExperienceGrid {...defaultProps} />, {
wrapper: IntlProvider,
});

expect(queryByTestId('metricsInfoError')).not.toBeInTheDocument();
expect(getByTestId('metricsExperienceProgressBar')).toBeInTheDocument();
});

it('shows and updates the search input when the search button is clicked', () => {
jest.useFakeTimers();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { EmptyState } from '../../empty_state/empty_state';
import { useToolbarActions } from '../../toolbar/hooks/use_toolbar_actions';
import { SearchButton } from '../../toolbar/right_side_actions/search_button';
import { MetricsExperienceGridContent } from './metrics_experience_grid_content';
import { MetricsInfoError } from './metrics_info_error';
import type { Dimension, UnifiedMetricsGridProps } from '../../../types';
import { useDiscoverFieldForBreakdown, useMetricFieldsFilter } from './hooks';

Expand Down Expand Up @@ -48,6 +49,7 @@ export const MetricsExperienceGrid = ({
metricItems,
allDimensions,
loading: isDiscoverLoading,
error: metricsInfoError,
} = useFetchMetricsData({
fetchParams,
services,
Expand Down Expand Up @@ -118,6 +120,15 @@ export const MetricsExperienceGrid = ({
return <EmptyState isLoading={isDiscoverLoading} />;
}

const isMetricsInfoAbortError =
metricsInfoError instanceof Error && metricsInfoError.name === 'AbortError';
const showMetricsInfoError =
metricsInfoError != null && !isDiscoverLoading && !isMetricsInfoAbortError;

if (showMetricsInfoError) {
return <MetricsInfoError />;
}

return (
<ChartsGrid
id="metricsExperienceGrid"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import React from 'react';
import { EuiEmptyPrompt, EuiIcon, EuiText, useEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';

/**
* METRICS_INFO failure state. Layout aligned with Discover’s `ErrorCallout` (EuiEmptyPrompt):
* icon on top, bordered card, title + description (fixed copy — not the raw error message).
*/
export const MetricsInfoError = () => {
const { euiTheme } = useEuiTheme();

return (
<EuiEmptyPrompt
data-test-subj="metricsInfoError"
icon={<EuiIcon size="l" type="error" color="danger" aria-hidden={true} />}
color="plain"
paddingSize="m"
hasBorder
titleSize="xs"
title={
<h2 data-test-subj="metricsInfoErrorTitle">
{i18n.translate('metricsExperience.metricsInfoError.title', {
defaultMessage: 'Unable to load visualization',
})}
</h2>
}
body={
<EuiText size="s" textAlign="center" data-test-subj="metricsInfoErrorDescription">
{i18n.translate('metricsExperience.metricsInfoError.description', {
defaultMessage:
"We're having some trouble retrieving the information needed for this visualization right now. Please wait a few moments or try refreshing the page.",
})}
</EuiText>
}
css={css`
margin: ${euiTheme.size.xl} auto;
`}
/>
);
};
Loading