mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[SLO] Add Auto-Refresh button on SLO List (#152460)
This commit is contained in:
parent
b3c3461fe1
commit
cb96fea0d9
10 changed files with 165 additions and 13 deletions
|
@ -20,6 +20,8 @@ export const useFetchHistoricalSummary = ({
|
|||
|
||||
return {
|
||||
isLoading: false,
|
||||
isInitialLoading: false,
|
||||
isRefetching: false,
|
||||
isSuccess: false,
|
||||
isError: false,
|
||||
sloHistoricalSummaryResponse: data,
|
||||
|
|
|
@ -10,10 +10,10 @@ import { FetchHistoricalSummaryResponse } from '@kbn/slo-schema';
|
|||
|
||||
import { useKibana } from '../../utils/kibana_react';
|
||||
|
||||
const EMPTY_RESPONSE: FetchHistoricalSummaryResponse = {};
|
||||
|
||||
export interface UseFetchHistoricalSummaryResponse {
|
||||
sloHistoricalSummaryResponse: FetchHistoricalSummaryResponse;
|
||||
sloHistoricalSummaryResponse: FetchHistoricalSummaryResponse | undefined;
|
||||
isInitialLoading: boolean;
|
||||
isRefetching: boolean;
|
||||
isLoading: boolean;
|
||||
isSuccess: boolean;
|
||||
isError: boolean;
|
||||
|
@ -49,8 +49,10 @@ export function useFetchHistoricalSummary({
|
|||
});
|
||||
|
||||
return {
|
||||
sloHistoricalSummaryResponse: isInitialLoading ? EMPTY_RESPONSE : data ?? EMPTY_RESPONSE,
|
||||
isLoading: isInitialLoading || isLoading || isRefetching,
|
||||
sloHistoricalSummaryResponse: data,
|
||||
isLoading,
|
||||
isRefetching,
|
||||
isInitialLoading,
|
||||
isSuccess,
|
||||
isError,
|
||||
};
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
QueryObserverResult,
|
||||
RefetchOptions,
|
||||
RefetchQueryFilters,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
} from '@tanstack/react-query';
|
||||
import { FindSLOResponse } from '@kbn/slo-schema';
|
||||
|
||||
|
@ -20,6 +22,7 @@ interface SLOListParams {
|
|||
page?: number;
|
||||
sortBy?: string;
|
||||
indicatorTypes?: string[];
|
||||
shouldRefetch?: boolean;
|
||||
}
|
||||
|
||||
export interface UseFetchSloListResponse {
|
||||
|
@ -33,13 +36,20 @@ export interface UseFetchSloListResponse {
|
|||
) => Promise<QueryObserverResult<FindSLOResponse | undefined, unknown>>;
|
||||
}
|
||||
|
||||
const SHORT_REFETCH_INTERVAL = 1000 * 5; // 5 seconds
|
||||
const LONG_REFETCH_INTERVAL = 1000 * 60; // 1 minute
|
||||
|
||||
export function useFetchSloList({
|
||||
name = '',
|
||||
page = 1,
|
||||
sortBy = 'name',
|
||||
indicatorTypes = [],
|
||||
shouldRefetch,
|
||||
}: SLOListParams | undefined = {}): UseFetchSloListResponse {
|
||||
const { http } = useKibana().services;
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const [stateRefetchInterval, setStateRefetchInterval] = useState(SHORT_REFETCH_INTERVAL);
|
||||
|
||||
const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data, refetch } = useQuery(
|
||||
{
|
||||
|
@ -64,9 +74,29 @@ export function useFetchSloList({
|
|||
// ignore error
|
||||
}
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
keepPreviousData: true,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchInterval: shouldRefetch ? stateRefetchInterval : undefined,
|
||||
staleTime: 1000,
|
||||
onSuccess: ({ results }: FindSLOResponse) => {
|
||||
if (!shouldRefetch) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (results.find((slo) => slo.summary.status === 'NO_DATA')) {
|
||||
setStateRefetchInterval(SHORT_REFETCH_INTERVAL);
|
||||
} else {
|
||||
setStateRefetchInterval(LONG_REFETCH_INTERVAL);
|
||||
}
|
||||
|
||||
queryClient.invalidateQueries(['fetchHistoricalSummary'], {
|
||||
exact: false,
|
||||
});
|
||||
|
||||
queryClient.invalidateQueries(['fetchActiveAlerts'], {
|
||||
exact: false,
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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, { useState } from 'react';
|
||||
import { ComponentStory } from '@storybook/react';
|
||||
|
||||
import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator';
|
||||
import { AutoRefreshButton as Component } from './auto_refresh_button';
|
||||
|
||||
export default {
|
||||
component: Component,
|
||||
title: 'app/SLO/ListPage/AutoRefreshButton',
|
||||
decorators: [KibanaReactStorybookDecorator],
|
||||
};
|
||||
|
||||
const Template: ComponentStory<typeof Component> = () => {
|
||||
const [isAutoRefreshing, setIsAutoRefreshing] = useState(true);
|
||||
|
||||
const toggleEnabled = () => {
|
||||
setIsAutoRefreshing(!isAutoRefreshing);
|
||||
};
|
||||
|
||||
return <Component isAutoRefreshing={isAutoRefreshing} onClick={toggleEnabled} />;
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
enabled: true,
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
export const AutoRefreshButton = Template.bind({});
|
||||
AutoRefreshButton.args = defaultProps;
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { EuiButtonEmpty } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
interface Props {
|
||||
isAutoRefreshing: boolean;
|
||||
dataTestSubj?: string;
|
||||
disabled?: boolean;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export function AutoRefreshButton({
|
||||
dataTestSubj = 'autoRefreshButton',
|
||||
disabled,
|
||||
isAutoRefreshing,
|
||||
onClick,
|
||||
}: Props) {
|
||||
return isAutoRefreshing ? (
|
||||
<EuiButtonEmpty
|
||||
data-test-subj={dataTestSubj}
|
||||
disabled={disabled}
|
||||
iconSide="left"
|
||||
iconType="pause"
|
||||
onClick={onClick}
|
||||
>
|
||||
{i18n.translate('xpack.observability.slosPage.stopRefreshingButtonLabel', {
|
||||
defaultMessage: 'Stop refreshing',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
) : (
|
||||
<EuiButtonEmpty
|
||||
data-test-subj={dataTestSubj}
|
||||
disabled={disabled}
|
||||
iconSide="left"
|
||||
iconType="play"
|
||||
onClick={onClick}
|
||||
>
|
||||
{i18n.translate('xpack.observability.slosPage.autoRefreshButtonLabel', {
|
||||
defaultMessage: 'Auto-refresh',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
}
|
|
@ -9,7 +9,7 @@ import React from 'react';
|
|||
import { ComponentStory } from '@storybook/react';
|
||||
|
||||
import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator';
|
||||
import { SloList as Component } from './slo_list';
|
||||
import { SloList as Component, Props } from './slo_list';
|
||||
|
||||
export default {
|
||||
component: Component,
|
||||
|
@ -18,9 +18,11 @@ export default {
|
|||
decorators: [KibanaReactStorybookDecorator],
|
||||
};
|
||||
|
||||
const Template: ComponentStory<typeof Component> = () => <Component />;
|
||||
const Template: ComponentStory<typeof Component> = (props: Props) => <Component {...props} />;
|
||||
|
||||
const defaultProps = {};
|
||||
const defaultProps = {
|
||||
autoRefresh: true,
|
||||
};
|
||||
|
||||
export const SloList = Template.bind({});
|
||||
SloList.args = defaultProps;
|
||||
|
|
|
@ -18,7 +18,11 @@ import {
|
|||
} from './slo_list_search_filter_sort_bar';
|
||||
import { SloListItems } from './slo_list_items';
|
||||
|
||||
export function SloList() {
|
||||
export interface Props {
|
||||
autoRefresh: boolean;
|
||||
}
|
||||
|
||||
export function SloList({ autoRefresh }: Props) {
|
||||
const [activePage, setActivePage] = useState(0);
|
||||
|
||||
const [query, setQuery] = useState('');
|
||||
|
@ -30,6 +34,7 @@ export function SloList() {
|
|||
name: query,
|
||||
sortBy: sort,
|
||||
indicatorTypes: indicatorTypeFilter,
|
||||
shouldRefetch: autoRefresh,
|
||||
});
|
||||
|
||||
const { results = [], total = 0, perPage = 0 } = sloList || {};
|
||||
|
|
|
@ -41,7 +41,7 @@ export function SloListItems({ sloList, loading, error }: Props) {
|
|||
<EuiFlexItem key={slo.id}>
|
||||
<SloListItem
|
||||
slo={slo}
|
||||
historicalSummary={sloHistoricalSummaryResponse[slo.id]}
|
||||
historicalSummary={sloHistoricalSummaryResponse?.[slo.id]}
|
||||
historicalSummaryLoading={historicalSummaryLoading}
|
||||
activeAlerts={activeAlertsBySlo[slo.id]}
|
||||
/>
|
||||
|
|
|
@ -155,6 +155,21 @@ describe('SLOs Page', () => {
|
|||
expect(screen.getByText('Create new SLO')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have an Auto Refresh button', async () => {
|
||||
useFetchSloListMock.mockReturnValue({ isLoading: false, sloList });
|
||||
|
||||
useFetchHistoricalSummaryMock.mockReturnValue({
|
||||
isLoading: false,
|
||||
sloHistoricalSummaryResponse: historicalSummaryData,
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
render(<SlosPage />, config);
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('autoRefreshButton')).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('when API has returned results', () => {
|
||||
it('renders the SLO list with SLO items', async () => {
|
||||
useFetchSloListMock.mockReturnValue({ isLoading: false, sloList });
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
|
@ -17,6 +17,7 @@ import { useCapabilities } from '../../hooks/slo/use_capabilities';
|
|||
import { useFetchSloList } from '../../hooks/slo/use_fetch_slo_list';
|
||||
import { SloList } from './components/slo_list';
|
||||
import { SloListWelcomePrompt } from './components/slo_list_welcome_prompt';
|
||||
import { AutoRefreshButton } from './components/auto_refresh_button';
|
||||
import PageNotFound from '../404';
|
||||
import { paths } from '../../config';
|
||||
import { isSloFeatureEnabled } from './helpers/is_slo_feature_enabled';
|
||||
|
@ -35,6 +36,8 @@ export function SlosPage() {
|
|||
|
||||
const { total } = sloList || {};
|
||||
|
||||
const [isAutoRefreshing, setIsAutoRefreshing] = useState<boolean>(true);
|
||||
|
||||
useBreadcrumbs([
|
||||
{
|
||||
href: basePath.prepend(paths.observability.slos),
|
||||
|
@ -48,6 +51,10 @@ export function SlosPage() {
|
|||
navigateToUrl(basePath.prepend(paths.observability.sloCreate));
|
||||
};
|
||||
|
||||
const handleToggleAutoRefresh = () => {
|
||||
setIsAutoRefreshing(!isAutoRefreshing);
|
||||
};
|
||||
|
||||
if (!isSloFeatureEnabled(config)) {
|
||||
return <PageNotFound />;
|
||||
}
|
||||
|
@ -78,12 +85,16 @@ export function SlosPage() {
|
|||
defaultMessage: 'Create new SLO',
|
||||
})}
|
||||
</EuiButton>,
|
||||
<AutoRefreshButton
|
||||
isAutoRefreshing={isAutoRefreshing}
|
||||
onClick={handleToggleAutoRefresh}
|
||||
/>,
|
||||
],
|
||||
bottomBorder: false,
|
||||
}}
|
||||
data-test-subj="slosPage"
|
||||
>
|
||||
<SloList />
|
||||
<SloList autoRefresh={isAutoRefreshing} />
|
||||
</ObservabilityPageTemplate>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue