feat(slo): handles tab from url in slo details page (#161222)

This commit is contained in:
Kevin Delemme 2023-07-11 20:23:38 -04:00 committed by GitHub
parent 06f7cbf9b6
commit ea0aed276d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 50 additions and 33 deletions

View file

@ -24,6 +24,6 @@ export const useFetchHistoricalSummary = ({
isRefetching: false,
isSuccess: false,
isError: false,
sloHistoricalSummaryResponse: data,
data,
};
};

View file

@ -12,7 +12,7 @@ import { useKibana } from '../../utils/kibana_react';
import { sloKeys } from './query_key_factory';
export interface UseFetchHistoricalSummaryResponse {
sloHistoricalSummaryResponse: FetchHistoricalSummaryResponse | undefined;
data: FetchHistoricalSummaryResponse | undefined;
isInitialLoading: boolean;
isRefetching: boolean;
isLoading: boolean;
@ -55,7 +55,7 @@ export function useFetchHistoricalSummary({
});
return {
sloHistoricalSummaryResponse: data,
data,
isLoading,
isRefetching,
isInitialLoading,

View file

@ -14,9 +14,10 @@ import {
EuiTabbedContentTab,
} from '@elastic/eui';
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
import React, { Fragment } from 'react';
import React, { Fragment, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { useLocation } from 'react-router-dom';
import { useFetchActiveAlerts } from '../../../hooks/slo/use_fetch_active_alerts';
import { formatHistoricalData } from '../../../utils/slo/chart_data_formatter';
import { useFetchHistoricalSummary } from '../../../hooks/slo/use_fetch_historical_summary';
@ -30,25 +31,28 @@ export interface Props {
slo: SLOWithSummaryResponse;
isAutoRefreshing: boolean;
}
const OVERVIEW_TAB = 'overview';
const ALERTS_TAB = 'alerts';
const TAB_ID_URL_PARAM = 'tabId';
const OVERVIEW_TAB_ID = 'overview';
const ALERTS_TAB_ID = 'alerts';
type TabId = typeof OVERVIEW_TAB_ID | typeof ALERTS_TAB_ID;
export function SloDetails({ slo, isAutoRefreshing }: Props) {
const { data: activeAlerts } = useFetchActiveAlerts({
sloIds: [slo.id],
});
const { isLoading: historicalSummaryLoading, sloHistoricalSummaryResponse = {} } =
const { search } = useLocation();
const { data: activeAlerts } = useFetchActiveAlerts({ sloIds: [slo.id] });
const { isLoading: historicalSummaryLoading, data: historicalSummaryBySlo = {} } =
useFetchHistoricalSummary({ sloIds: [slo.id], shouldRefetch: isAutoRefreshing });
const errorBudgetBurnDownData = formatHistoricalData(
sloHistoricalSummaryResponse[slo.id],
historicalSummaryBySlo[slo.id],
'error_budget_remaining'
);
const historicalSliData = formatHistoricalData(sloHistoricalSummaryResponse[slo.id], 'sli_value');
const historicalSliData = formatHistoricalData(historicalSummaryBySlo[slo.id], 'sli_value');
const tabs: EuiTabbedContentTab[] = [
{
id: OVERVIEW_TAB,
id: OVERVIEW_TAB_ID,
name: i18n.translate('xpack.observability.slo.sloDetails.tab.overviewLabel', {
defaultMessage: 'Overview',
}),
@ -84,7 +88,7 @@ export function SloDetails({ slo, isAutoRefreshing }: Props) {
),
},
{
id: ALERTS_TAB,
id: ALERTS_TAB_ID,
name: i18n.translate('xpack.observability.slo.sloDetails.tab.alertsLabel', {
defaultMessage: 'Alerts',
}),
@ -98,11 +102,24 @@ export function SloDetails({ slo, isAutoRefreshing }: Props) {
},
];
const [selectedTabId, setSelectedTabId] = useState(() => {
const searchParams = new URLSearchParams(search);
const urlTabId = searchParams.get(TAB_ID_URL_PARAM);
return urlTabId && [OVERVIEW_TAB_ID, ALERTS_TAB_ID].includes(urlTabId)
? (urlTabId as TabId)
: OVERVIEW_TAB_ID;
});
const handleSelectedTab = (newTabId: TabId) => {
setSelectedTabId(newTabId);
};
return (
<EuiTabbedContent
data-test-subj="sloDetailsTabbedContent"
tabs={tabs}
initialSelectedTab={tabs[0]}
selectedTab={tabs.find((tab) => tab.id === selectedTabId) ?? tabs[0]}
onTabClick={(tab) => handleSelectedTab(tab.id as TabId)}
/>
);
}

View file

@ -9,7 +9,7 @@ import React from 'react';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { useKibana } from '../../utils/kibana_react';
import { useParams } from 'react-router-dom';
import { useParams, useLocation } from 'react-router-dom';
import { useLicense } from '../../hooks/use_license';
import { useCapabilities } from '../../hooks/slo/use_capabilities';
import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details';
@ -31,6 +31,7 @@ import { buildApmAvailabilityIndicator } from '../../data/slo/indicator';
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useParams: jest.fn(),
useLocation: jest.fn(),
}));
jest.mock('@kbn/observability-shared-plugin/public');
@ -45,6 +46,7 @@ jest.mock('../../hooks/slo/use_delete_slo');
const useKibanaMock = useKibana as jest.Mock;
const useParamsMock = useParams as jest.Mock;
const useLocationMock = useLocation as jest.Mock;
const useLicenseMock = useLicense as jest.Mock;
const useCapabilitiesMock = useCapabilities as jest.Mock;
const useFetchActiveAlertsMock = useFetchActiveAlerts as jest.Mock;
@ -105,11 +107,12 @@ describe('SLO Details Page', () => {
useCapabilitiesMock.mockReturnValue({ hasWriteCapabilities: true, hasReadCapabilities: true });
useFetchHistoricalSummaryMock.mockReturnValue({
isLoading: false,
sloHistoricalSummaryResponse: historicalSummaryData,
data: historicalSummaryData,
});
useFetchActiveAlertsMock.mockReturnValue({ isLoading: false, data: {} });
useCloneSloMock.mockReturnValue({ mutate: mockClone });
useDeleteSloMock.mockReturnValue({ mutate: mockDelete });
useLocationMock.mockReturnValue({ search: '' });
});
describe('when the incorrect license is found', () => {
@ -155,7 +158,7 @@ describe('SLO Details Page', () => {
useLicenseMock.mockReturnValue({ hasAtLeast: () => true });
useFetchHistoricalSummaryMock.mockReturnValue({
isLoading: true,
sloHistoricalSummaryResponse: {},
data: {},
});
render(<SloDetailsPage />);

View file

@ -39,11 +39,8 @@ export function SloDetailsPage() {
const hasRightLicense = hasAtLeast('platinum');
const { sloId } = useParams<SloDetailsPathParams>();
const [isAutoRefreshing, setIsAutoRefreshing] = useState(true);
const { isLoading, slo } = useFetchSloDetails({ sloId, shouldRefetch: isAutoRefreshing });
const isCloningOrDeleting = Boolean(useIsMutating());
useBreadcrumbs(getBreadcrumbs(basePath, slo));

View file

@ -27,7 +27,7 @@ export function SloListItems({ sloList, loading, error }: Props) {
const { data: activeAlertsBySlo } = useFetchActiveAlerts({ sloIds });
const { data: rulesBySlo } = useFetchRulesForSlo({ sloIds });
const { isLoading: historicalSummaryLoading, sloHistoricalSummaryResponse } =
const { isLoading: historicalSummaryLoading, data: historicalSummaryBySlo } =
useFetchHistoricalSummary({ sloIds });
const { mutate: deleteSlo } = useDeleteSlo();
@ -50,7 +50,7 @@ export function SloListItems({ sloList, loading, error }: Props) {
<SloListItem
activeAlerts={activeAlertsBySlo[slo.id]}
rules={rulesBySlo?.[slo.id]}
historicalSummary={sloHistoricalSummaryResponse?.[slo.id]}
historicalSummary={historicalSummaryBySlo?.[slo.id]}
historicalSummaryLoading={historicalSummaryLoading}
slo={slo}
onConfirmDelete={handleDelete}

View file

@ -112,7 +112,7 @@ describe('SLOs Page', () => {
useLicenseMock.mockReturnValue({ hasAtLeast: () => false });
useFetchHistoricalSummaryMock.mockReturnValue({
isLoading: false,
sloHistoricalSummaryResponse: {},
data: {},
});
});
it('navigates to the SLOs Welcome Page', async () => {
@ -135,7 +135,7 @@ describe('SLOs Page', () => {
useFetchSloListMock.mockReturnValue({ isLoading: false, sloList: emptySloList });
useFetchHistoricalSummaryMock.mockReturnValue({
isLoading: false,
sloHistoricalSummaryResponse: {},
data: {},
});
await act(async () => {
@ -152,7 +152,7 @@ describe('SLOs Page', () => {
useFetchHistoricalSummaryMock.mockReturnValue({
isLoading: false,
sloHistoricalSummaryResponse: historicalSummaryData,
data: historicalSummaryData,
});
await act(async () => {
@ -167,7 +167,7 @@ describe('SLOs Page', () => {
useFetchHistoricalSummaryMock.mockReturnValue({
isLoading: false,
sloHistoricalSummaryResponse: historicalSummaryData,
data: historicalSummaryData,
});
await act(async () => {
@ -183,7 +183,7 @@ describe('SLOs Page', () => {
useFetchHistoricalSummaryMock.mockReturnValue({
isLoading: false,
sloHistoricalSummaryResponse: historicalSummaryData,
data: historicalSummaryData,
});
await act(async () => {
@ -201,7 +201,7 @@ describe('SLOs Page', () => {
useFetchHistoricalSummaryMock.mockReturnValue({
isLoading: false,
sloHistoricalSummaryResponse: historicalSummaryData,
data: historicalSummaryData,
});
await act(async () => {
@ -228,7 +228,7 @@ describe('SLOs Page', () => {
useFetchHistoricalSummaryMock.mockReturnValue({
isLoading: false,
sloHistoricalSummaryResponse: historicalSummaryData,
data: historicalSummaryData,
});
await act(async () => {
@ -253,7 +253,7 @@ describe('SLOs Page', () => {
useFetchHistoricalSummaryMock.mockReturnValue({
isLoading: false,
sloHistoricalSummaryResponse: historicalSummaryData,
data: historicalSummaryData,
});
await act(async () => {
@ -278,7 +278,7 @@ describe('SLOs Page', () => {
useFetchHistoricalSummaryMock.mockReturnValue({
isLoading: false,
sloHistoricalSummaryResponse: historicalSummaryData,
data: historicalSummaryData,
});
await act(async () => {
@ -308,7 +308,7 @@ describe('SLOs Page', () => {
useFetchHistoricalSummaryMock.mockReturnValue({
isLoading: false,
sloHistoricalSummaryResponse: historicalSummaryData,
data: historicalSummaryData,
});
await act(async () => {