From 2d6b0a52bba23e76519eea4e8f95cd60bdc8b899 Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Tue, 10 Oct 2023 11:22:23 -0400 Subject: [PATCH 01/11] Use data convention for use fetch slo list --- .../public/embeddable/slo/overview/slo_selector.tsx | 6 +++++- .../slo/__storybook_mocks__/use_fetch_slo_list.ts | 2 +- .../public/hooks/slo/use_fetch_slo_list.ts | 11 ++++------- .../public/pages/slos/components/slo_list.tsx | 9 +++++++-- .../plugins/observability/public/pages/slos/slos.tsx | 4 ++-- .../public/pages/slos_welcome/slos_welcome.tsx | 4 ++-- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/observability/public/embeddable/slo/overview/slo_selector.tsx b/x-pack/plugins/observability/public/embeddable/slo/overview/slo_selector.tsx index 468358127bd18..8ad4985e0096d 100644 --- a/x-pack/plugins/observability/public/embeddable/slo/overview/slo_selector.tsx +++ b/x-pack/plugins/observability/public/embeddable/slo/overview/slo_selector.tsx @@ -26,7 +26,11 @@ export function SloSelector({ initialSlo, onSelected, hasError }: Props) { const [options, setOptions] = useState>>([]); const [selectedOptions, setSelectedOptions] = useState>>(); const [searchValue, setSearchValue] = useState(''); - const { isInitialLoading, isLoading, sloList } = useFetchSloList({ + const { + isInitialLoading, + isLoading, + data: sloList, + } = useFetchSloList({ kqlQuery: `slo.name: ${searchValue.replaceAll(' ', '*')}*`, }); diff --git a/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_slo_list.ts b/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_slo_list.ts index 2faa0887b82ea..4e7d1889e5bb0 100644 --- a/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_slo_list.ts +++ b/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_slo_list.ts @@ -15,7 +15,7 @@ export const useFetchSloList = (): UseFetchSloListResponse => { isRefetching: false, isError: false, isSuccess: true, - sloList, + data: sloList, refetch: function () {} as UseFetchSloListResponse['refetch'], }; }; diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts index 22a918eb23127..a4a67b0aecceb 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts @@ -33,7 +33,7 @@ export interface UseFetchSloListResponse { isRefetching: boolean; isSuccess: boolean; isError: boolean; - sloList: FindSLOResponse | undefined; + data: FindSLOResponse | undefined; refetch: ( options?: (RefetchOptions & RefetchQueryFilters) | undefined ) => Promise>; @@ -48,16 +48,13 @@ export function useFetchSloList({ sortBy = 'status', sortDirection = 'desc', shouldRefetch, -}: SLOListParams | undefined = {}): UseFetchSloListResponse { +}: SLOListParams = {}): UseFetchSloListResponse { const { http, notifications: { toasts }, } = useKibana().services; const queryClient = useQueryClient(); - - const [stateRefetchInterval, setStateRefetchInterval] = useState( - SHORT_REFETCH_INTERVAL - ); + const [stateRefetchInterval, setStateRefetchInterval] = useState(SHORT_REFETCH_INTERVAL); const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data, refetch } = useQuery( { @@ -115,7 +112,7 @@ export function useFetchSloList({ ); return { - sloList: data, + data, isInitialLoading, isLoading, isRefetching, diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx index 651a97d364439..59e68b4a06996 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx @@ -21,7 +21,12 @@ export function SloList({ autoRefresh }: Props) { const [query, setQuery] = useState(''); const [sort, setSort] = useState('status'); - const { isLoading, isRefetching, isError, sloList } = useFetchSloList({ + const { + isLoading, + isRefetching, + isError, + data: sloList, + } = useFetchSloList({ page: activePage + 1, kqlQuery: query, sortBy: sort, @@ -29,7 +34,7 @@ export function SloList({ autoRefresh }: Props) { shouldRefetch: autoRefresh, }); - const { results = [], total = 0, perPage = 0 } = sloList || {}; + const { results = [], total = 0, perPage = 0 } = sloList ?? {}; const isCreatingSlo = Boolean(useIsMutating(['creatingSlo'])); const isCloningSlo = Boolean(useIsMutating(['cloningSlo'])); diff --git a/x-pack/plugins/observability/public/pages/slos/slos.tsx b/x-pack/plugins/observability/public/pages/slos/slos.tsx index a183d7941f7e1..18fa418e4358b 100644 --- a/x-pack/plugins/observability/public/pages/slos/slos.tsx +++ b/x-pack/plugins/observability/public/pages/slos/slos.tsx @@ -32,8 +32,8 @@ export function SlosPage() { const { hasWriteCapabilities } = useCapabilities(); const { hasAtLeast } = useLicense(); - const { isInitialLoading, isLoading, isError, sloList } = useFetchSloList(); - const { total } = sloList || { total: 0 }; + const { isInitialLoading, isLoading, isError, data: sloList } = useFetchSloList(); + const { total } = sloList ?? { total: 0 }; const { storeAutoRefreshState, getAutoRefreshState } = useAutoRefreshStorage(); const [isAutoRefreshing, setIsAutoRefreshing] = useState(getAutoRefreshState()); diff --git a/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.tsx b/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.tsx index 5c4317cd4e138..16f4a75974453 100644 --- a/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.tsx +++ b/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.tsx @@ -40,8 +40,8 @@ export function SlosWelcomePage() { const { hasAtLeast } = useLicense(); const hasRightLicense = hasAtLeast('platinum'); - const { isLoading, sloList } = useFetchSloList(); - const { total } = sloList || { total: 0 }; + const { isLoading, data: sloList } = useFetchSloList(); + const { total } = sloList ?? { total: 0 }; const hasRequiredWritePrivileges = !!globalDiagnosis?.userPrivileges.write.has_all_requested; const hasRequiredReadPrivileges = !!globalDiagnosis?.userPrivileges.read.has_all_requested; From ff0d2d9453e4f4e2cdd9db76dff991c095ef2eab Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Tue, 10 Oct 2023 11:27:00 -0400 Subject: [PATCH 02/11] Use data convention for use fetch slo details --- .../burn_rate_rule_editor/burn_rate_rule_editor.tsx | 2 +- .../public/embeddable/slo/overview/slo_overview.tsx | 7 ++++++- .../public/hooks/slo/use_fetch_slo_details.ts | 6 +++--- .../observability/public/pages/slo_details/slo_details.tsx | 2 +- .../observability/public/pages/slo_edit/slo_edit.tsx | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/observability/public/components/burn_rate_rule_editor/burn_rate_rule_editor.tsx b/x-pack/plugins/observability/public/components/burn_rate_rule_editor/burn_rate_rule_editor.tsx index 7e0e88d6c148d..3aa7651320bc3 100644 --- a/x-pack/plugins/observability/public/components/burn_rate_rule_editor/burn_rate_rule_editor.tsx +++ b/x-pack/plugins/observability/public/components/burn_rate_rule_editor/burn_rate_rule_editor.tsx @@ -31,7 +31,7 @@ type Props = Pick< export function BurnRateRuleEditor(props: Props) { const { setRuleParams, ruleParams, errors } = props; - const { isLoading: loadingInitialSlo, slo: initialSlo } = useFetchSloDetails({ + const { isLoading: loadingInitialSlo, data: initialSlo } = useFetchSloDetails({ sloId: ruleParams?.sloId, }); diff --git a/x-pack/plugins/observability/public/embeddable/slo/overview/slo_overview.tsx b/x-pack/plugins/observability/public/embeddable/slo/overview/slo_overview.tsx index 5e8947a6c5ba9..392b0b1dcc400 100644 --- a/x-pack/plugins/observability/public/embeddable/slo/overview/slo_overview.tsx +++ b/x-pack/plugins/observability/public/embeddable/slo/overview/slo_overview.tsx @@ -26,7 +26,12 @@ export function SloOverview({ sloId, sloInstanceId, lastReloadRequestTime }: Emb application: { navigateToUrl }, http: { basePath }, } = useKibana().services; - const { isLoading, slo, refetch, isRefetching } = useFetchSloDetails({ + const { + isLoading, + data: slo, + refetch, + isRefetching, + } = useFetchSloDetails({ sloId, instanceId: sloInstanceId, }); diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_details.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_details.ts index 38f980ee1bdc9..ad5511433522e 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_details.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_details.ts @@ -5,13 +5,13 @@ * 2.0. */ +import { ALL_VALUE, GetSLOResponse } from '@kbn/slo-schema'; import { QueryObserverResult, RefetchOptions, RefetchQueryFilters, useQuery, } from '@tanstack/react-query'; -import { ALL_VALUE, GetSLOResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; import { useKibana } from '../../utils/kibana_react'; import { sloKeys } from './query_key_factory'; @@ -21,7 +21,7 @@ export interface UseFetchSloDetailsResponse { isRefetching: boolean; isSuccess: boolean; isError: boolean; - slo: SLOWithSummaryResponse | undefined; + data: GetSLOResponse | undefined; refetch: ( options?: (RefetchOptions & RefetchQueryFilters) | undefined ) => Promise>; @@ -65,7 +65,7 @@ export function useFetchSloDetails({ ); return { - slo: data, + data, isLoading, isInitialLoading, isRefetching, diff --git a/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx b/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx index 6b50fbde29268..195cb164e5197 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx @@ -45,7 +45,7 @@ export function SloDetailsPage() { const sloInstanceId = useGetInstanceIdQueryParam(); const { storeAutoRefreshState, getAutoRefreshState } = useAutoRefreshStorage(); const [isAutoRefreshing, setIsAutoRefreshing] = useState(getAutoRefreshState()); - const { isLoading, slo } = useFetchSloDetails({ + const { isLoading, data: slo } = useFetchSloDetails({ sloId, instanceId: sloInstanceId, shouldRefetch: isAutoRefreshing, diff --git a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx index a9da5c0c0ee97..1f5cc7f38ed3c 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx @@ -33,7 +33,7 @@ export function SloEditPage() { const { sloId } = useParams<{ sloId: string | undefined }>(); const { hasAtLeast } = useLicense(); const hasRightLicense = hasAtLeast('platinum'); - const { slo, isInitialLoading } = useFetchSloDetails({ sloId }); + const { data: slo, isInitialLoading } = useFetchSloDetails({ sloId }); useBreadcrumbs([ { From 51081ca0bae74c137740d4638b7d238bf3d006b8 Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Tue, 10 Oct 2023 12:44:59 -0400 Subject: [PATCH 03/11] Read state from url --- .../public/pages/slos/components/slo_list.tsx | 12 ++++-- .../slo_list_search_filter_sort_bar.tsx | 18 ++++++--- .../slos/hooks/use_store_search_state.ts | 39 +++++++++++++++++++ 3 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx index 59e68b4a06996..1d660d73cfbb9 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx @@ -9,6 +9,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiPagination } from '@elastic/eui'; import { useIsMutating } from '@tanstack/react-query'; import React, { useState } from 'react'; import { useFetchSloList } from '../../../hooks/slo/use_fetch_slo_list'; +import { useStoreSearchState } from '../hooks/use_store_search_state'; import { SloListItems } from './slo_list_items'; import { SloListSearchFilterSortBar, SortField } from './slo_list_search_filter_sort_bar'; @@ -17,9 +18,11 @@ export interface Props { } export function SloList({ autoRefresh }: Props) { - const [activePage, setActivePage] = useState(0); - const [query, setQuery] = useState(''); - const [sort, setSort] = useState('status'); + const { state } = useStoreSearchState(); + + const [activePage, setActivePage] = useState(state.page); + const [query, setQuery] = useState(state.kqlQuery); + const [sort, setSort] = useState(state.sort.by); const { isLoading, @@ -30,7 +33,7 @@ export function SloList({ autoRefresh }: Props) { page: activePage + 1, kqlQuery: query, sortBy: sort, - sortDirection: 'desc', + sortDirection: state.sort.direction, shouldRefetch: autoRefresh, }); @@ -62,6 +65,7 @@ export function SloList({ autoRefresh }: Props) { loading={isLoading || isCreatingSlo || isCloningSlo || isUpdatingSlo || isDeletingSlo} onChangeQuery={handleChangeQuery} onChangeSort={handleChangeSort} + initialState={state} /> diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_filter_sort_bar.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_filter_sort_bar.tsx index c1d9c6b3c3a07..8b85e1d9187d9 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_filter_sort_bar.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_filter_sort_bar.tsx @@ -22,9 +22,11 @@ import { QueryStringInput } from '@kbn/unified-search-plugin/public'; import React, { useState } from 'react'; import { useCreateDataView } from '../../../hooks/use_create_data_view'; import { useKibana } from '../../../utils/kibana_react'; +import { SearchState } from '../hooks/use_store_search_state'; export interface SloListSearchFilterSortBarProps { loading: boolean; + initialState: SearchState; onChangeQuery: (query: string) => void; onChangeSort: (sort: SortField | undefined) => void; } @@ -49,7 +51,6 @@ const SORT_OPTIONS: Array> = [ defaultMessage: 'SLO status', }), type: 'status', - checked: 'on', }, { label: i18n.translate('xpack.observability.slo.list.sortBy.errorBudgetConsumed', { @@ -69,18 +70,23 @@ export function SloListSearchFilterSortBar({ loading, onChangeQuery, onChangeSort, + initialState, }: SloListSearchFilterSortBarProps) { const { data, dataViews, docLinks, http, notifications, storage, uiSettings, unifiedSearch } = useKibana().services; const { dataView } = useCreateDataView({ indexPatternString: '.slo-observability.summary-*' }); + const [query, setQuery] = useState(initialState.kqlQuery); const [isSortPopoverOpen, setSortPopoverOpen] = useState(false); - const [sortOptions, setSortOptions] = useState(SORT_OPTIONS); - const [query, setQuery] = useState(''); - + const [sortOptions, setSortOptions] = useState>>( + SORT_OPTIONS.map((option) => ({ + ...option, + checked: option.type === initialState.sort.by ? 'on' : undefined, + })) + ); const selectedSort = sortOptions.find((option) => option.checked === 'on'); - const handleToggleSortButton = () => setSortPopoverOpen(!isSortPopoverOpen); + const handleToggleSortButton = () => setSortPopoverOpen(!isSortPopoverOpen); const handleChangeSort = (newOptions: Array>) => { setSortOptions(newOptions); setSortPopoverOpen(false); @@ -133,7 +139,7 @@ export function SloListSearchFilterSortBar({ > {i18n.translate('xpack.observability.slo.list.sortByType', { defaultMessage: 'Sort by {type}', - values: { type: selectedSort?.label.toLowerCase() || '' }, + values: { type: selectedSort?.label.toLowerCase() ?? '' }, })} } diff --git a/x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts b/x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts new file mode 100644 index 0000000000000..a0c1baff87d85 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts @@ -0,0 +1,39 @@ +/* + * 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 { useHistory } from 'react-router-dom'; +import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; +import deepmerge from 'deepmerge'; +import { SortField } from '../components/slo_list_search_filter_sort_bar'; + +export interface SearchState { + kqlQuery: string; + page: number; + sort: { + by: SortField; + direction: 'asc' | 'desc'; + }; +} + +const DEFAULT_STATE = { + kqlQuery: '', + page: 0, + sort: { by: 'status' as const, direction: 'desc' as const }, +}; + +export function useStoreSearchState(): { state: SearchState } { + const history = useHistory(); + const urlStateStorage = createKbnUrlStateStorage({ + history, + useHash: false, + useHashQuery: false, + }); + + const searchState = urlStateStorage.get('changeme') ?? DEFAULT_STATE; + + return { state: deepmerge(DEFAULT_STATE, searchState) }; +} From 82c401cccd930f659b92b4a395a9d46dc659973a Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Tue, 10 Oct 2023 12:48:54 -0400 Subject: [PATCH 04/11] Rename search bar component --- .../public/pages/slos/components/slo_list.tsx | 4 ++-- ...es.tsx => slo_list_search_bar.stories.tsx} | 19 ++++++++----------- ...r_sort_bar.tsx => slo_list_search_bar.tsx} | 9 ++------- .../slos/hooks/use_store_search_state.ts | 4 ++-- 4 files changed, 14 insertions(+), 22 deletions(-) rename x-pack/plugins/observability/public/pages/slos/components/{slo_list_search_filter_sort_bar.stories.tsx => slo_list_search_bar.stories.tsx} (55%) rename x-pack/plugins/observability/public/pages/slos/components/{slo_list_search_filter_sort_bar.tsx => slo_list_search_bar.tsx} (96%) diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx index 1d660d73cfbb9..2c7574cf44842 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx @@ -11,7 +11,7 @@ import React, { useState } from 'react'; import { useFetchSloList } from '../../../hooks/slo/use_fetch_slo_list'; import { useStoreSearchState } from '../hooks/use_store_search_state'; import { SloListItems } from './slo_list_items'; -import { SloListSearchFilterSortBar, SortField } from './slo_list_search_filter_sort_bar'; +import { SloListSearchBar, SortField } from './slo_list_search_bar'; export interface Props { autoRefresh: boolean; @@ -61,7 +61,7 @@ export function SloList({ autoRefresh }: Props) { return ( - = (props: SloListSearchFilterSortBarProps) => ( - -); +const Template: ComponentStory = (props: Props) => ; -const defaultProps: SloListSearchFilterSortBarProps = { +const defaultProps: Props = { loading: false, onChangeQuery: () => {}, onChangeSort: () => {}, + initialState: DEFAULT_STATE, }; -export const SloListSearchFilterSortBar = Template.bind({}); -SloListSearchFilterSortBar.args = defaultProps; +export const SloListSearchBar = Template.bind({}); +SloListSearchBar.args = defaultProps; diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_filter_sort_bar.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx similarity index 96% rename from x-pack/plugins/observability/public/pages/slos/components/slo_list_search_filter_sort_bar.tsx rename to x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx index 8b85e1d9187d9..104231668e09f 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_filter_sort_bar.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx @@ -24,7 +24,7 @@ import { useCreateDataView } from '../../../hooks/use_create_data_view'; import { useKibana } from '../../../utils/kibana_react'; import { SearchState } from '../hooks/use_store_search_state'; -export interface SloListSearchFilterSortBarProps { +export interface Props { loading: boolean; initialState: SearchState; onChangeQuery: (query: string) => void; @@ -66,12 +66,7 @@ const SORT_OPTIONS: Array> = [ }, ]; -export function SloListSearchFilterSortBar({ - loading, - onChangeQuery, - onChangeSort, - initialState, -}: SloListSearchFilterSortBarProps) { +export function SloListSearchBar({ loading, onChangeQuery, onChangeSort, initialState }: Props) { const { data, dataViews, docLinks, http, notifications, storage, uiSettings, unifiedSearch } = useKibana().services; const { dataView } = useCreateDataView({ indexPatternString: '.slo-observability.summary-*' }); diff --git a/x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts b/x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts index a0c1baff87d85..1d1a5f01070fa 100644 --- a/x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts +++ b/x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts @@ -8,7 +8,7 @@ import { useHistory } from 'react-router-dom'; import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import deepmerge from 'deepmerge'; -import { SortField } from '../components/slo_list_search_filter_sort_bar'; +import { SortField } from '../components/slo_list_search_bar'; export interface SearchState { kqlQuery: string; @@ -19,7 +19,7 @@ export interface SearchState { }; } -const DEFAULT_STATE = { +export const DEFAULT_STATE = { kqlQuery: '', page: 0, sort: { by: 'status' as const, direction: 'desc' as const }, From 3497624dc3ae2e30b49234b7f2500d7b0b6018ce Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Tue, 10 Oct 2023 12:51:05 -0400 Subject: [PATCH 05/11] Rename page --- .../public/pages/slos/components/slo_list.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx index 2c7574cf44842..e207443a6bd39 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx @@ -20,7 +20,7 @@ export interface Props { export function SloList({ autoRefresh }: Props) { const { state } = useStoreSearchState(); - const [activePage, setActivePage] = useState(state.page); + const [page, setPage] = useState(state.page); const [query, setQuery] = useState(state.kqlQuery); const [sort, setSort] = useState(state.sort.by); @@ -30,7 +30,7 @@ export function SloList({ autoRefresh }: Props) { isError, data: sloList, } = useFetchSloList({ - page: activePage + 1, + page: page + 1, kqlQuery: query, sortBy: sort, sortDirection: state.sort.direction, @@ -45,16 +45,16 @@ export function SloList({ autoRefresh }: Props) { const isDeletingSlo = Boolean(useIsMutating(['deleteSlo'])); const handlePageClick = (pageNumber: number) => { - setActivePage(pageNumber); + setPage(pageNumber); }; const handleChangeQuery = (newQuery: string) => { - setActivePage(0); + setPage(0); setQuery(newQuery); }; const handleChangeSort = (newSort: SortField | undefined) => { - setActivePage(0); + setPage(0); setSort(newSort); }; @@ -79,7 +79,7 @@ export function SloList({ autoRefresh }: Props) { From 6a1ca18d728876cc85292edc45c6bd81f0ca6f6d Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Tue, 10 Oct 2023 13:01:15 -0400 Subject: [PATCH 06/11] Store state in url --- .../public/pages/slos/components/slo_list.tsx | 15 +++++++++------ .../pages/slos/components/slo_list_search_bar.tsx | 6 +++--- .../pages/slos/hooks/use_store_search_state.ts | 13 ++++++++++--- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx index e207443a6bd39..d12f760eddae0 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx @@ -18,7 +18,7 @@ export interface Props { } export function SloList({ autoRefresh }: Props) { - const { state } = useStoreSearchState(); + const { state, store } = useStoreSearchState(); const [page, setPage] = useState(state.page); const [query, setQuery] = useState(state.kqlQuery); @@ -30,7 +30,7 @@ export function SloList({ autoRefresh }: Props) { isError, data: sloList, } = useFetchSloList({ - page: page + 1, + page, kqlQuery: query, sortBy: sort, sortDirection: state.sort.direction, @@ -46,16 +46,19 @@ export function SloList({ autoRefresh }: Props) { const handlePageClick = (pageNumber: number) => { setPage(pageNumber); + store({ page: pageNumber }); }; const handleChangeQuery = (newQuery: string) => { - setPage(0); + setPage(1); setQuery(newQuery); + store({ page: 1, kqlQuery: newQuery }); }; - const handleChangeSort = (newSort: SortField | undefined) => { - setPage(0); + const handleChangeSort = (newSort: SortField) => { + setPage(1); setSort(newSort); + store({ page: 1, sort: { by: newSort, direction: state.sort.direction } }); }; return ( @@ -79,7 +82,7 @@ export function SloList({ autoRefresh }: Props) { diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx index 104231668e09f..1093e32e33ef8 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx @@ -28,7 +28,7 @@ export interface Props { loading: boolean; initialState: SearchState; onChangeQuery: (query: string) => void; - onChangeSort: (sort: SortField | undefined) => void; + onChangeSort: (sort: SortField) => void; } export type SortField = 'sli_value' | 'error_budget_consumed' | 'error_budget_remaining' | 'status'; @@ -85,7 +85,7 @@ export function SloListSearchBar({ loading, onChangeQuery, onChangeSort, initial const handleChangeSort = (newOptions: Array>) => { setSortOptions(newOptions); setSortPopoverOpen(false); - onChangeSort(newOptions.find((o) => o.checked)?.type); + onChangeSort(newOptions.find((o) => o.checked)!.type); }; return ( @@ -150,7 +150,7 @@ export function SloListSearchBar({ loading, onChangeQuery, onChangeSort, initial })} > - singleSelection + singleSelection="always" options={sortOptions} onChange={handleChangeSort} isLoading={loading} diff --git a/x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts b/x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts index 1d1a5f01070fa..b4e61da3ac82a 100644 --- a/x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts +++ b/x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts @@ -21,11 +21,14 @@ export interface SearchState { export const DEFAULT_STATE = { kqlQuery: '', - page: 0, + page: 1, sort: { by: 'status' as const, direction: 'desc' as const }, }; -export function useStoreSearchState(): { state: SearchState } { +export function useStoreSearchState(): { + state: SearchState; + store: (state: Partial) => Promise; +} { const history = useHistory(); const urlStateStorage = createKbnUrlStateStorage({ history, @@ -35,5 +38,9 @@ export function useStoreSearchState(): { state: SearchState } { const searchState = urlStateStorage.get('changeme') ?? DEFAULT_STATE; - return { state: deepmerge(DEFAULT_STATE, searchState) }; + return { + state: deepmerge(DEFAULT_STATE, searchState), + store: (state: Partial) => + urlStateStorage.set('changeme', deepmerge(searchState, state), { replace: true }), + }; } From 95874de7164c25df18112aa9236d50f7d40ecfaf Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Tue, 10 Oct 2023 13:02:29 -0400 Subject: [PATCH 07/11] Rename hook --- .../public/pages/slos/components/slo_list.tsx | 10 +++++----- .../slos/components/slo_list_search_bar.stories.tsx | 2 +- .../pages/slos/components/slo_list_search_bar.tsx | 2 +- .../{use_store_search_state.ts => use_search_state.ts} | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) rename x-pack/plugins/observability/public/pages/slos/hooks/{use_store_search_state.ts => use_search_state.ts} (83%) diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx index d12f760eddae0..41eacb25e732d 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx @@ -9,7 +9,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiPagination } from '@elastic/eui'; import { useIsMutating } from '@tanstack/react-query'; import React, { useState } from 'react'; import { useFetchSloList } from '../../../hooks/slo/use_fetch_slo_list'; -import { useStoreSearchState } from '../hooks/use_store_search_state'; +import { useSearchState as useSearchState } from '../hooks/use_search_state'; import { SloListItems } from './slo_list_items'; import { SloListSearchBar, SortField } from './slo_list_search_bar'; @@ -18,7 +18,7 @@ export interface Props { } export function SloList({ autoRefresh }: Props) { - const { state, store } = useStoreSearchState(); + const { state, store: storeState } = useSearchState(); const [page, setPage] = useState(state.page); const [query, setQuery] = useState(state.kqlQuery); @@ -46,19 +46,19 @@ export function SloList({ autoRefresh }: Props) { const handlePageClick = (pageNumber: number) => { setPage(pageNumber); - store({ page: pageNumber }); + storeState({ page: pageNumber }); }; const handleChangeQuery = (newQuery: string) => { setPage(1); setQuery(newQuery); - store({ page: 1, kqlQuery: newQuery }); + storeState({ page: 1, kqlQuery: newQuery }); }; const handleChangeSort = (newSort: SortField) => { setPage(1); setSort(newSort); - store({ page: 1, sort: { by: newSort, direction: state.sort.direction } }); + storeState({ page: 1, sort: { by: newSort, direction: state.sort.direction } }); }; return ( diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.stories.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.stories.tsx index 003c72918e633..b563c5c75b807 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.stories.tsx @@ -10,7 +10,7 @@ import { ComponentStory } from '@storybook/react'; import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; import { SloListSearchBar as Component, Props } from './slo_list_search_bar'; -import { DEFAULT_STATE } from '../hooks/use_store_search_state'; +import { DEFAULT_STATE } from '../hooks/use_search_state'; export default { component: Component, diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx index 1093e32e33ef8..6abea538b68c5 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx @@ -22,7 +22,7 @@ import { QueryStringInput } from '@kbn/unified-search-plugin/public'; import React, { useState } from 'react'; import { useCreateDataView } from '../../../hooks/use_create_data_view'; import { useKibana } from '../../../utils/kibana_react'; -import { SearchState } from '../hooks/use_store_search_state'; +import { SearchState } from '../hooks/use_search_state'; export interface Props { loading: boolean; diff --git a/x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts b/x-pack/plugins/observability/public/pages/slos/hooks/use_search_state.ts similarity index 83% rename from x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts rename to x-pack/plugins/observability/public/pages/slos/hooks/use_search_state.ts index b4e61da3ac82a..26b77dd96f466 100644 --- a/x-pack/plugins/observability/public/pages/slos/hooks/use_store_search_state.ts +++ b/x-pack/plugins/observability/public/pages/slos/hooks/use_search_state.ts @@ -25,7 +25,7 @@ export const DEFAULT_STATE = { sort: { by: 'status' as const, direction: 'desc' as const }, }; -export function useStoreSearchState(): { +export function useSearchState(): { state: SearchState; store: (state: Partial) => Promise; } { @@ -36,11 +36,11 @@ export function useStoreSearchState(): { useHashQuery: false, }); - const searchState = urlStateStorage.get('changeme') ?? DEFAULT_STATE; + const searchState = urlStateStorage.get('search') ?? DEFAULT_STATE; return { state: deepmerge(DEFAULT_STATE, searchState), store: (state: Partial) => - urlStateStorage.set('changeme', deepmerge(searchState, state), { replace: true }), + urlStateStorage.set('search', deepmerge(searchState, state), { replace: true }), }; } From 2488a4866c2536873d5857f591a3853379f6eadd Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Tue, 10 Oct 2023 13:06:07 -0400 Subject: [PATCH 08/11] Fix page --- .../public/pages/slos/components/slo_list.tsx | 34 +++++++++---------- .../pages/slos/hooks/use_search_state.ts | 2 +- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx index 41eacb25e732d..a4ddcf821c880 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx @@ -30,7 +30,7 @@ export function SloList({ autoRefresh }: Props) { isError, data: sloList, } = useFetchSloList({ - page, + page: page + 1, kqlQuery: query, sortBy: sort, sortDirection: state.sort.direction, @@ -50,15 +50,15 @@ export function SloList({ autoRefresh }: Props) { }; const handleChangeQuery = (newQuery: string) => { - setPage(1); + setPage(0); setQuery(newQuery); - storeState({ page: 1, kqlQuery: newQuery }); + storeState({ page: 0, kqlQuery: newQuery }); }; const handleChangeSort = (newSort: SortField) => { - setPage(1); + setPage(0); setSort(newSort); - storeState({ page: 1, sort: { by: newSort, direction: state.sort.direction } }); + storeState({ page: 0, sort: { by: newSort, direction: state.sort.direction } }); }; return ( @@ -76,19 +76,17 @@ export function SloList({ autoRefresh }: Props) { - {results.length ? ( - - - - - - - - ) : null} + + + + + + + ); } diff --git a/x-pack/plugins/observability/public/pages/slos/hooks/use_search_state.ts b/x-pack/plugins/observability/public/pages/slos/hooks/use_search_state.ts index 26b77dd96f466..da244ac31e5a0 100644 --- a/x-pack/plugins/observability/public/pages/slos/hooks/use_search_state.ts +++ b/x-pack/plugins/observability/public/pages/slos/hooks/use_search_state.ts @@ -21,7 +21,7 @@ export interface SearchState { export const DEFAULT_STATE = { kqlQuery: '', - page: 1, + page: 0, sort: { by: 'status' as const, direction: 'desc' as const }, }; From e166a2157d61e2b97ee48325cc5e231b1c28c302 Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Tue, 10 Oct 2023 14:13:34 -0400 Subject: [PATCH 09/11] Rename hook --- .../public/pages/slos/components/slo_list.tsx | 10 +++++----- .../slos/components/slo_list_search_bar.stories.tsx | 2 +- .../pages/slos/components/slo_list_search_bar.tsx | 2 +- .../{use_search_state.ts => use_url_search_state.ts} | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) rename x-pack/plugins/observability/public/pages/slos/hooks/{use_search_state.ts => use_url_search_state.ts} (97%) diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx index a4ddcf821c880..e4db368d18cab 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx @@ -9,7 +9,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiPagination } from '@elastic/eui'; import { useIsMutating } from '@tanstack/react-query'; import React, { useState } from 'react'; import { useFetchSloList } from '../../../hooks/slo/use_fetch_slo_list'; -import { useSearchState as useSearchState } from '../hooks/use_search_state'; +import { useUrlSearchState } from '../hooks/use_url_search_state'; import { SloListItems } from './slo_list_items'; import { SloListSearchBar, SortField } from './slo_list_search_bar'; @@ -18,11 +18,11 @@ export interface Props { } export function SloList({ autoRefresh }: Props) { - const { state, store: storeState } = useSearchState(); - + const { state, store: storeState } = useUrlSearchState(); const [page, setPage] = useState(state.page); const [query, setQuery] = useState(state.kqlQuery); - const [sort, setSort] = useState(state.sort.by); + const [sort, setSort] = useState(state.sort.by); + const [direction] = useState<'asc' | 'desc'>(state.sort.direction); const { isLoading, @@ -33,7 +33,7 @@ export function SloList({ autoRefresh }: Props) { page: page + 1, kqlQuery: query, sortBy: sort, - sortDirection: state.sort.direction, + sortDirection: direction, shouldRefetch: autoRefresh, }); diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.stories.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.stories.tsx index b563c5c75b807..fb39f46e81e96 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.stories.tsx @@ -10,7 +10,7 @@ import { ComponentStory } from '@storybook/react'; import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; import { SloListSearchBar as Component, Props } from './slo_list_search_bar'; -import { DEFAULT_STATE } from '../hooks/use_search_state'; +import { DEFAULT_STATE } from '../hooks/use_url_search_state'; export default { component: Component, diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx index 6abea538b68c5..dc6aebfd3eae7 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_search_bar.tsx @@ -22,7 +22,7 @@ import { QueryStringInput } from '@kbn/unified-search-plugin/public'; import React, { useState } from 'react'; import { useCreateDataView } from '../../../hooks/use_create_data_view'; import { useKibana } from '../../../utils/kibana_react'; -import { SearchState } from '../hooks/use_search_state'; +import { SearchState } from '../hooks/use_url_search_state'; export interface Props { loading: boolean; diff --git a/x-pack/plugins/observability/public/pages/slos/hooks/use_search_state.ts b/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts similarity index 97% rename from x-pack/plugins/observability/public/pages/slos/hooks/use_search_state.ts rename to x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts index da244ac31e5a0..2d7fb860900fa 100644 --- a/x-pack/plugins/observability/public/pages/slos/hooks/use_search_state.ts +++ b/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts @@ -25,7 +25,7 @@ export const DEFAULT_STATE = { sort: { by: 'status' as const, direction: 'desc' as const }, }; -export function useSearchState(): { +export function useUrlSearchState(): { state: SearchState; store: (state: Partial) => Promise; } { From 5136544023f4c2d6be5df42d6720f64263a4c8c7 Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Tue, 10 Oct 2023 14:37:15 -0400 Subject: [PATCH 10/11] Fix tests after hook update --- .../pages/slo_details/slo_details.test.tsx | 26 ++++++------ .../public/pages/slo_edit/slo_edit.test.tsx | 22 +++++----- .../public/pages/slos/slos.test.tsx | 41 +++++++++---------- .../pages/slos_welcome/slos_welcome.test.tsx | 6 +-- 4 files changed, 47 insertions(+), 48 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx b/x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx index 5dad78763fe04..f31d36c822264 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx @@ -124,7 +124,7 @@ describe('SLO Details Page', () => { it('navigates to the SLO List page', async () => { const slo = buildSlo(); useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloDetailsMock.mockReturnValue({ isLoading: false, data: slo }); useLicenseMock.mockReturnValue({ hasAtLeast: () => false }); render(); @@ -135,7 +135,7 @@ describe('SLO Details Page', () => { it('renders the PageNotFound when the SLO cannot be found', async () => { useParamsMock.mockReturnValue('nonexistent'); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo: undefined }); + useFetchSloDetailsMock.mockReturnValue({ isLoading: false, data: undefined }); useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); render(); @@ -146,7 +146,7 @@ describe('SLO Details Page', () => { it('renders the loading spinner when fetching the SLO', async () => { const slo = buildSlo(); useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: true, slo: undefined }); + useFetchSloDetailsMock.mockReturnValue({ isLoading: true, data: undefined }); useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); render(); @@ -159,7 +159,7 @@ describe('SLO Details Page', () => { it('renders the SLO details page with loading charts when summary data is loading', async () => { const slo = buildSlo({ id: HEALTHY_STEP_DOWN_ROLLING_SLO }); useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloDetailsMock.mockReturnValue({ isLoading: false, data: slo }); useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); useFetchHistoricalSummaryMock.mockReturnValue({ isLoading: true, @@ -178,7 +178,7 @@ describe('SLO Details Page', () => { it('renders the SLO details page with the overview and chart panels', async () => { const slo = buildSlo({ id: HEALTHY_STEP_DOWN_ROLLING_SLO }); useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloDetailsMock.mockReturnValue({ isLoading: false, data: slo }); useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); render(); @@ -194,7 +194,7 @@ describe('SLO Details Page', () => { it("renders a 'Edit' button under actions menu", async () => { const slo = buildSlo(); useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloDetailsMock.mockReturnValue({ isLoading: false, data: slo }); useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); render(); @@ -206,7 +206,7 @@ describe('SLO Details Page', () => { it("renders a 'Create alert rule' button under actions menu", async () => { const slo = buildSlo(); useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloDetailsMock.mockReturnValue({ isLoading: false, data: slo }); useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); render(); @@ -218,7 +218,7 @@ describe('SLO Details Page', () => { it("renders a 'Manage rules' button under actions menu", async () => { const slo = buildSlo(); useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloDetailsMock.mockReturnValue({ isLoading: false, data: slo }); useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); render(); @@ -230,7 +230,7 @@ describe('SLO Details Page', () => { it("renders a 'Clone' button under actions menu", async () => { const slo = buildSlo(); useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloDetailsMock.mockReturnValue({ isLoading: false, data: slo }); useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); render(); @@ -271,7 +271,7 @@ describe('SLO Details Page', () => { it("renders a 'Delete' button under actions menu", async () => { const slo = buildSlo(); useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloDetailsMock.mockReturnValue({ isLoading: false, data: slo }); useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); render(); @@ -301,7 +301,7 @@ describe('SLO Details Page', () => { it('renders the Overview tab by default', async () => { const slo = buildSlo(); useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloDetailsMock.mockReturnValue({ isLoading: false, data: slo }); useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); useFetchActiveAlertsMock.mockReturnValue({ isLoading: false, @@ -320,7 +320,7 @@ describe('SLO Details Page', () => { it("renders a 'Explore in APM' button under actions menu", async () => { const slo = buildSlo({ indicator: buildApmAvailabilityIndicator() }); useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloDetailsMock.mockReturnValue({ isLoading: false, data: slo }); useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); render(); @@ -334,7 +334,7 @@ describe('SLO Details Page', () => { it("does not render a 'Explore in APM' button under actions menu", async () => { const slo = buildSlo(); useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloDetailsMock.mockReturnValue({ isLoading: false, data: slo }); useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); render(); diff --git a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.test.tsx b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.test.tsx index c38689200a164..b158094a67aab 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.test.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.test.tsx @@ -147,7 +147,7 @@ describe('SLO Edit Page', () => { .spyOn(Router, 'useLocation') .mockReturnValue({ pathname: 'foo', search: '', state: '', hash: '' }); - useFetchSloMock.mockReturnValue({ isLoading: false, slo: undefined }); + useFetchSloMock.mockReturnValue({ isLoading: false, data: undefined }); useFetchIndicesMock.mockReturnValue({ isLoading: false, @@ -201,7 +201,7 @@ describe('SLO Edit Page', () => { .spyOn(Router, 'useLocation') .mockReturnValue({ pathname: 'foo', search: '', state: '', hash: '' }); - useFetchSloMock.mockReturnValue({ isLoading: false, slo: undefined }); + useFetchSloMock.mockReturnValue({ isLoading: false, data: undefined }); useFetchIndicesMock.mockReturnValue({ isLoading: false, @@ -237,7 +237,7 @@ describe('SLO Edit Page', () => { .spyOn(Router, 'useLocation') .mockReturnValue({ pathname: 'foo', search: '', state: '', hash: '' }); - useFetchSloMock.mockReturnValue({ isLoading: false, slo: undefined }); + useFetchSloMock.mockReturnValue({ isLoading: false, data: undefined }); useFetchIndicesMock.mockReturnValue({ isLoading: false, @@ -287,7 +287,7 @@ describe('SLO Edit Page', () => { data: ['some-index'], }); - useFetchSloMock.mockReturnValue({ isLoading: false, slo: undefined }); + useFetchSloMock.mockReturnValue({ isLoading: false, data: undefined }); const mockCreate = jest.fn(); const mockUpdate = jest.fn(); @@ -377,7 +377,7 @@ describe('SLO Edit Page', () => { .spyOn(Router, 'useLocation') .mockReturnValue({ pathname: 'foo', search: '', state: '', hash: '' }); - useFetchSloMock.mockReturnValue({ isLoading: false, slo: undefined }); + useFetchSloMock.mockReturnValue({ isLoading: false, data: undefined }); useFetchApmSuggestionsMock.mockReturnValue({ suggestions: ['cartService'], @@ -428,7 +428,7 @@ describe('SLO Edit Page', () => { .spyOn(Router, 'useLocation') .mockReturnValue({ pathname: 'foo', search: '', state: '', hash: '' }); - useFetchSloMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloMock.mockReturnValue({ isLoading: false, data: slo }); useFetchIndicesMock.mockReturnValue({ isLoading: false, @@ -496,7 +496,7 @@ describe('SLO Edit Page', () => { data: ['some-index'], }); - useFetchSloMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloMock.mockReturnValue({ isLoading: false, data: slo }); const mockCreate = jest.fn(); const mockUpdate = jest.fn(); @@ -537,7 +537,7 @@ describe('SLO Edit Page', () => { .spyOn(Router, 'useLocation') .mockReturnValue({ pathname: 'foo', search: '', state: '', hash: '' }); - useFetchSloMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloMock.mockReturnValue({ isLoading: false, data: slo }); useFetchApmSuggestionsMock.mockReturnValue({ suggestions: ['cartService'], @@ -607,7 +607,7 @@ describe('SLO Edit Page', () => { .spyOn(Router, 'useLocation') .mockReturnValue({ pathname: 'foo', search: '', state: '', hash: '' }); - useFetchSloMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloMock.mockReturnValue({ isLoading: false, data: slo }); useFetchIndicesMock.mockReturnValue({ isLoading: false, @@ -648,7 +648,7 @@ describe('SLO Edit Page', () => { .spyOn(Router, 'useLocation') .mockReturnValue({ pathname: 'foo', search: '', state: '', hash: '' }); - useFetchSloMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloMock.mockReturnValue({ isLoading: false, data: slo }); useFetchIndicesMock.mockReturnValue({ isLoading: false, @@ -693,7 +693,7 @@ describe('SLO Edit Page', () => { .spyOn(Router, 'useLocation') .mockReturnValue({ pathname: 'foo', search: 'create-rule=true', state: '', hash: '' }); - useFetchSloMock.mockReturnValue({ isLoading: false, slo }); + useFetchSloMock.mockReturnValue({ isLoading: false, data: slo }); useFetchIndicesMock.mockReturnValue({ isLoading: false, diff --git a/x-pack/plugins/observability/public/pages/slos/slos.test.tsx b/x-pack/plugins/observability/public/pages/slos/slos.test.tsx index 09d5af66b55dc..3e19b7a466be5 100644 --- a/x-pack/plugins/observability/public/pages/slos/slos.test.tsx +++ b/x-pack/plugins/observability/public/pages/slos/slos.test.tsx @@ -5,25 +5,25 @@ * 2.0. */ +import { act, screen, waitFor } from '@testing-library/react'; import React from 'react'; -import { screen, act, waitFor } from '@testing-library/react'; -import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; +import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; -import { render } from '../../utils/test_helper'; -import { useKibana } from '../../utils/kibana_react'; -import { useCreateSlo } from '../../hooks/slo/use_create_slo'; +import { paths } from '../../../common/locators/paths'; +import { historicalSummaryData } from '../../data/slo/historical_summary_data'; +import { emptySloList, sloList } from '../../data/slo/slo'; +import { useCapabilities } from '../../hooks/slo/use_capabilities'; import { useCloneSlo } from '../../hooks/slo/use_clone_slo'; +import { useCreateSlo } from '../../hooks/slo/use_create_slo'; import { useDeleteSlo } from '../../hooks/slo/use_delete_slo'; -import { useFetchSloList } from '../../hooks/slo/use_fetch_slo_list'; import { useFetchHistoricalSummary } from '../../hooks/slo/use_fetch_historical_summary'; +import { useFetchSloList } from '../../hooks/slo/use_fetch_slo_list'; import { useLicense } from '../../hooks/use_license'; +import { useKibana } from '../../utils/kibana_react'; +import { render } from '../../utils/test_helper'; import { SlosPage } from './slos'; -import { emptySloList, sloList } from '../../data/slo/slo'; -import { historicalSummaryData } from '../../data/slo/historical_summary_data'; -import { useCapabilities } from '../../hooks/slo/use_capabilities'; -import { paths } from '../../../common/locators/paths'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -51,11 +51,10 @@ const useCapabilitiesMock = useCapabilities as jest.Mock; const mockCreateSlo = jest.fn(); const mockCloneSlo = jest.fn(); +const mockDeleteSlo = jest.fn(); useCreateSloMock.mockReturnValue({ mutate: mockCreateSlo }); useCloneSloMock.mockReturnValue({ mutate: mockCloneSlo }); - -const mockDeleteSlo = jest.fn(); useDeleteSloMock.mockReturnValue({ mutate: mockDeleteSlo }); const mockNavigate = jest.fn(); @@ -155,7 +154,7 @@ describe('SLOs Page', () => { }); it('navigates to the SLOs Welcome Page when the API has finished loading and there are no results', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList: emptySloList }); + useFetchSloListMock.mockReturnValue({ isLoading: false, data: emptySloList }); useFetchHistoricalSummaryMock.mockReturnValue({ isLoading: false, data: {}, @@ -171,7 +170,7 @@ describe('SLOs Page', () => { }); it('should have a create new SLO button', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); + useFetchSloListMock.mockReturnValue({ isLoading: false, data: sloList }); useFetchHistoricalSummaryMock.mockReturnValue({ isLoading: false, @@ -186,7 +185,7 @@ describe('SLOs Page', () => { }); it('should have an Auto Refresh button', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); + useFetchSloListMock.mockReturnValue({ isLoading: false, data: sloList }); useFetchHistoricalSummaryMock.mockReturnValue({ isLoading: false, @@ -202,7 +201,7 @@ describe('SLOs Page', () => { describe('when API has returned results', () => { it('renders the SLO list with SLO items', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); + useFetchSloListMock.mockReturnValue({ isLoading: false, data: sloList }); useFetchHistoricalSummaryMock.mockReturnValue({ isLoading: false, @@ -220,7 +219,7 @@ describe('SLOs Page', () => { }); it('allows editing an SLO', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); + useFetchSloListMock.mockReturnValue({ isLoading: false, data: sloList }); useFetchHistoricalSummaryMock.mockReturnValue({ isLoading: false, @@ -247,7 +246,7 @@ describe('SLOs Page', () => { }); it('allows creating a new rule for an SLO', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); + useFetchSloListMock.mockReturnValue({ isLoading: false, data: sloList }); useFetchHistoricalSummaryMock.mockReturnValue({ isLoading: false, @@ -272,7 +271,7 @@ describe('SLOs Page', () => { }); it('allows managing rules for an SLO', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); + useFetchSloListMock.mockReturnValue({ isLoading: false, data: sloList }); useFetchHistoricalSummaryMock.mockReturnValue({ isLoading: false, @@ -297,7 +296,7 @@ describe('SLOs Page', () => { }); it('allows deleting an SLO', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); + useFetchSloListMock.mockReturnValue({ isLoading: false, data: sloList }); useFetchHistoricalSummaryMock.mockReturnValue({ isLoading: false, @@ -327,7 +326,7 @@ describe('SLOs Page', () => { }); it('allows cloning an SLO', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); + useFetchSloListMock.mockReturnValue({ isLoading: false, data: sloList }); useFetchHistoricalSummaryMock.mockReturnValue({ isLoading: false, diff --git a/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.test.tsx b/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.test.tsx index fbeae4cb872b2..a6b4f671d306c 100644 --- a/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.test.tsx +++ b/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.test.tsx @@ -56,7 +56,7 @@ describe('SLOs Welcome Page', () => { describe('when the incorrect license is found', () => { it('renders the welcome message with subscription buttons', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList: emptySloList }); + useFetchSloListMock.mockReturnValue({ isLoading: false, data: emptySloList }); useLicenseMock.mockReturnValue({ hasAtLeast: () => false }); useGlobalDiagnosisMock.mockReturnValue({ data: { @@ -82,7 +82,7 @@ describe('SLOs Welcome Page', () => { describe('when loading is done and no results are found', () => { beforeEach(() => { - useFetchSloListMock.mockReturnValue({ isLoading: false, emptySloList }); + useFetchSloListMock.mockReturnValue({ isLoading: false, data: emptySloList }); }); it('disables the create slo button when no write capabilities', async () => { @@ -146,7 +146,7 @@ describe('SLOs Welcome Page', () => { describe('when loading is done and results are found', () => { beforeEach(() => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); + useFetchSloListMock.mockReturnValue({ isLoading: false, data: sloList }); useGlobalDiagnosisMock.mockReturnValue({ data: { userPrivileges: { From c56e29dcc4420862bae24128e191d9d4d5f6df3f Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Tue, 10 Oct 2023 14:50:07 -0400 Subject: [PATCH 11/11] Show pagination only when there is some results --- .../public/pages/slos/components/slo_list.tsx | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx index e4db368d18cab..380d0100db1a1 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx @@ -71,22 +71,23 @@ export function SloList({ autoRefresh }: Props) { initialState={state} /> - - - - - - - - + {total > 0 ? ( + + + + + + + + ) : null} ); }