mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
feat(slo): handles tab from url in slo details page (#161222)
This commit is contained in:
parent
06f7cbf9b6
commit
ea0aed276d
7 changed files with 50 additions and 33 deletions
|
@ -24,6 +24,6 @@ export const useFetchHistoricalSummary = ({
|
|||
isRefetching: false,
|
||||
isSuccess: false,
|
||||
isError: false,
|
||||
sloHistoricalSummaryResponse: data,
|
||||
data,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 />);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue