-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[8.4] [Kubernetes Security] - Tree Navigation Empty State (#137133)
- Loading branch information
Showing
12 changed files
with
295 additions
and
83 deletions.
There are no files selected for viewing
1 change: 1 addition & 0 deletions
1
...tree_view_container/assets/illustration_product_no_results_magnifying_glass.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions
37
x-pack/plugins/kubernetes_security/public/components/tree_view_container/contexts.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* | ||
* 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, { createContext, useContext } from 'react'; | ||
|
||
import { useTreeView, UseTreeViewProps } from './hooks'; | ||
|
||
type TreeViewContextType = ReturnType<typeof useTreeView>; | ||
|
||
const TreeViewContext = createContext<TreeViewContextType | null>(null); | ||
|
||
export const useTreeViewContext = () => { | ||
const context = useContext(TreeViewContext); | ||
if (!context) { | ||
throw new Error('useTreeViewContext must be called within an TreeViewContextProvider'); | ||
} | ||
return context; | ||
}; | ||
|
||
type TreeViewContextProviderProps = { | ||
children: JSX.Element; | ||
}; | ||
|
||
export const TreeViewContextProvider = ({ | ||
children, | ||
...useTreeViewProps | ||
}: TreeViewContextProviderProps & UseTreeViewProps) => { | ||
return ( | ||
<TreeViewContext.Provider value={useTreeView(useTreeViewProps)}> | ||
{children} | ||
</TreeViewContext.Provider> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
x-pack/plugins/kubernetes_security/public/components/tree_view_container/empty_state.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* 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 { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiImage, EuiText, EuiTitle } from '@elastic/eui'; | ||
import { FormattedMessage } from '@kbn/i18n-react'; | ||
import { CSSObject } from '@emotion/serialize'; | ||
import icon from './assets/illustration_product_no_results_magnifying_glass.svg'; | ||
|
||
export const TREE_EMPTY_STATE = 'kubernetesSecurity:treeEmptyState'; | ||
|
||
const panelStyle: CSSObject = { | ||
maxWidth: 500, | ||
}; | ||
|
||
const wrapperStyle: CSSObject = { | ||
height: 262, | ||
}; | ||
|
||
export const EmptyState: React.FC = () => { | ||
return ( | ||
<EuiPanel color="subdued" data-test-subj={TREE_EMPTY_STATE}> | ||
<EuiFlexGroup css={wrapperStyle} alignItems="center" justifyContent="center"> | ||
<EuiFlexItem grow={false}> | ||
<EuiPanel hasBorder={true} css={panelStyle}> | ||
<EuiFlexGroup> | ||
<EuiFlexItem> | ||
<EuiText size="s"> | ||
<EuiTitle> | ||
<h3> | ||
<FormattedMessage | ||
id="xpack.kubernetesSecurity.treeView.empty.title" | ||
defaultMessage="No results match your search criteria" | ||
/> | ||
</h3> | ||
</EuiTitle> | ||
<p> | ||
<FormattedMessage | ||
id="xpack.kubernetesSecurity.treeView.empty.description" | ||
defaultMessage="Try searching over a longer period of time or modifying your search" | ||
/> | ||
</p> | ||
</EuiText> | ||
</EuiFlexItem> | ||
<EuiFlexItem grow={false}> | ||
<EuiImage size="200" alt="" url={icon} /> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</EuiPanel> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</EuiPanel> | ||
); | ||
}; |
63 changes: 63 additions & 0 deletions
63
x-pack/plugins/kubernetes_security/public/components/tree_view_container/hooks.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/* | ||
* 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 { useCallback, useEffect, useMemo, useState } from 'react'; | ||
import { KubernetesCollection, TreeNavSelection } from '../../types'; | ||
import { addTimerangeAndDefaultFilterToQuery } from '../../utils/add_timerange_and_default_filter_to_query'; | ||
import { addTreeNavSelectionToFilterQuery } from './helpers'; | ||
import { IndexPattern, GlobalFilter } from '../../types'; | ||
|
||
export type UseTreeViewProps = { | ||
globalFilter: GlobalFilter; | ||
indexPattern?: IndexPattern; | ||
}; | ||
|
||
export const useTreeView = ({ globalFilter, indexPattern }: UseTreeViewProps) => { | ||
const [noResults, setNoResults] = useState(false); | ||
const [treeNavSelection, setTreeNavSelection] = useState<TreeNavSelection>({}); | ||
|
||
const filterQueryWithTimeRange = useMemo(() => { | ||
return JSON.parse( | ||
addTimerangeAndDefaultFilterToQuery( | ||
globalFilter.filterQuery, | ||
globalFilter.startDate, | ||
globalFilter.endDate | ||
) | ||
); | ||
}, [globalFilter.filterQuery, globalFilter.startDate, globalFilter.endDate]); | ||
|
||
const onTreeNavSelect = useCallback((selection: TreeNavSelection) => { | ||
setTreeNavSelection(selection); | ||
}, []); | ||
|
||
const hasSelection = useMemo( | ||
() => !!treeNavSelection[KubernetesCollection.cluster], | ||
[treeNavSelection] | ||
); | ||
|
||
const sessionViewFilter = useMemo( | ||
() => addTreeNavSelectionToFilterQuery(globalFilter.filterQuery, treeNavSelection), | ||
[globalFilter.filterQuery, treeNavSelection] | ||
); | ||
|
||
// Resetting defaults whenever filter changes | ||
useEffect(() => { | ||
setNoResults(false); | ||
setTreeNavSelection({}); | ||
}, [filterQueryWithTimeRange]); | ||
|
||
return { | ||
noResults, | ||
setNoResults, | ||
filterQueryWithTimeRange, | ||
indexPattern: indexPattern?.title || '', | ||
onTreeNavSelect, | ||
hasSelection, | ||
treeNavSelection, | ||
sessionViewFilter, | ||
}; | ||
}; |
43 changes: 43 additions & 0 deletions
43
x-pack/plugins/kubernetes_security/public/components/tree_view_container/index.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* | ||
* 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 { TreeViewContainer } from '.'; | ||
import { AppContextTestRender, createAppRootMockRenderer } from '../../test'; | ||
import * as context from './contexts'; | ||
|
||
describe('TreeNav component', () => { | ||
let render: () => ReturnType<AppContextTestRender['render']>; | ||
let renderResult: ReturnType<typeof render>; | ||
let mockedContext: AppContextTestRender; | ||
const spy = jest.spyOn(context, 'useTreeViewContext'); | ||
|
||
const defaultProps = { | ||
globalFilter: { | ||
startDate: Date.now().toString(), | ||
endDate: (Date.now() + 1).toString(), | ||
}, | ||
renderSessionsView: <div>Session View</div>, | ||
} as any; | ||
|
||
beforeEach(() => { | ||
mockedContext = createAppRootMockRenderer(); | ||
}); | ||
afterEach(() => { | ||
spy.mockRestore(); | ||
}); | ||
|
||
it('shows empty message when there is no results', async () => { | ||
spy.mockImplementation(() => ({ | ||
...jest.requireActual('./contexts').useTreeViewContext, | ||
noResults: true, | ||
})); | ||
|
||
renderResult = mockedContext.render(<TreeViewContainer {...defaultProps} />); | ||
expect(await renderResult.getByText(/no results/i)).toBeInTheDocument(); | ||
}); | ||
}); |
Oops, something went wrong.