mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Asset Inventory] Add OnboardingSuccessCallout to the All Assets page (#216739)
## Summary It closes https://github.com/elastic/kibana/issues/210717 This PR adds the OnboardingSuccessCallout component to the All Assets page, but the component is only visible to the user who initiated the Onboarding and it no longer shows once dismissed by the user. Also, this PR adds the `checkAndInitAssetCriticalityResources` to the enablement as it's required by the transforms installed during the Entity Store initialization. ## Recording https://github.com/user-attachments/assets/31130195-c67c-4a55-aa37-555d527f38f0
This commit is contained in:
parent
cf289cbd1e
commit
1b3b66b6de
7 changed files with 63 additions and 2 deletions
|
@ -14,6 +14,7 @@ export const mockUseOnboardingSuccessCallout = (
|
|||
isOnboardingSuccessCalloutVisible: false,
|
||||
hideOnboardingSuccessCallout: jest.fn(),
|
||||
showOnboardingSuccessCallout: jest.fn(),
|
||||
onAddIntegrationClick: jest.fn(),
|
||||
...overrides,
|
||||
};
|
||||
return defaultMock;
|
||||
|
|
|
@ -11,6 +11,17 @@ import { useOnboardingSuccessCallout } from './use_onboarding_success_callout';
|
|||
|
||||
jest.mock('react-use/lib/useLocalStorage');
|
||||
const mockLocalStorage = [false, jest.fn()];
|
||||
const mockNavigateToApp = jest.fn();
|
||||
|
||||
jest.mock('../../../../common/lib/kibana', () => ({
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
application: {
|
||||
navigateToApp: mockNavigateToApp,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('useOnboardingSuccessCallout', () => {
|
||||
beforeEach(() => {
|
||||
|
@ -44,4 +55,12 @@ describe('useOnboardingSuccessCallout', () => {
|
|||
expect(mockLocalStorage[1]).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should call application.navigateToApp when onAddIntegrationClick is called', () => {
|
||||
const { result } = renderHook(() => useOnboardingSuccessCallout());
|
||||
|
||||
result.current.onAddIntegrationClick();
|
||||
|
||||
expect(mockNavigateToApp).toHaveBeenCalledWith('integrations');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
|
||||
import useLocalStorage from 'react-use/lib/useLocalStorage';
|
||||
import { INTEGRATIONS_PLUGIN_ID } from '@kbn/fleet-plugin/common';
|
||||
import { useKibana } from '../../../../common/lib/kibana';
|
||||
import { LOCAL_STORAGE_ONBOARDING_SUCCESS_CALLOUT_KEY } from '../../../constants';
|
||||
|
||||
/**
|
||||
|
@ -17,13 +19,18 @@ export const useOnboardingSuccessCallout = () => {
|
|||
const [isOnboardingSuccessCalloutVisible, setOnboardingSuccessCalloutVisible] =
|
||||
useLocalStorage<boolean>(LOCAL_STORAGE_ONBOARDING_SUCCESS_CALLOUT_KEY, false);
|
||||
|
||||
const { application } = useKibana().services;
|
||||
|
||||
const hideOnboardingSuccessCallout = () => setOnboardingSuccessCalloutVisible(false);
|
||||
|
||||
const showOnboardingSuccessCallout = () => setOnboardingSuccessCalloutVisible(true);
|
||||
|
||||
const onAddIntegrationClick = () => application.navigateToApp(INTEGRATIONS_PLUGIN_ID);
|
||||
|
||||
return {
|
||||
isOnboardingSuccessCalloutVisible,
|
||||
hideOnboardingSuccessCallout,
|
||||
showOnboardingSuccessCallout,
|
||||
onAddIntegrationClick,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -57,4 +57,21 @@ describe('OnboardingSuccessCallout', () => {
|
|||
expect(mockHideCallout).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should have an "Add integration" button that calls onAddIntegrationClick when clicked', async () => {
|
||||
const mockAddIntegrationClick = jest.fn();
|
||||
|
||||
(useOnboardingSuccessCallout as jest.Mock).mockReturnValue(
|
||||
mockUseOnboardingSuccessCallout({
|
||||
isOnboardingSuccessCalloutVisible: true,
|
||||
onAddIntegrationClick: mockAddIntegrationClick,
|
||||
})
|
||||
);
|
||||
|
||||
renderWithTestProvider(<OnboardingSuccessCallout />);
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: /add integration/i }));
|
||||
|
||||
expect(mockAddIntegrationClick).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,17 +5,22 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiButton, EuiCallOut, EuiSpacer } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useOnboardingSuccessCallout } from './hooks/use_onboarding_success_callout';
|
||||
import { TEST_SUBJ_ONBOARDING_SUCCESS_CALLOUT } from '../../constants';
|
||||
|
||||
/**
|
||||
* Component to display a success callout after onboarding is completed.
|
||||
* Only visible if the user enabled the onboarding process.
|
||||
*/
|
||||
export const OnboardingSuccessCallout = () => {
|
||||
const { isOnboardingSuccessCalloutVisible, hideOnboardingSuccessCallout } =
|
||||
const { isOnboardingSuccessCalloutVisible, hideOnboardingSuccessCallout, onAddIntegrationClick } =
|
||||
useOnboardingSuccessCallout();
|
||||
|
||||
return isOnboardingSuccessCalloutVisible ? (
|
||||
<>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiCallOut
|
||||
onDismiss={hideOnboardingSuccessCallout}
|
||||
title={
|
||||
|
@ -34,6 +39,12 @@ export const OnboardingSuccessCallout = () => {
|
|||
defaultMessage="Asset Inventory is now set up and ready to use. You can start managing your assets with enhanced visibility and context, empowering your security team to make informed decisions."
|
||||
/>
|
||||
</p>
|
||||
<EuiButton size="s" color="success" onClick={onAddIntegrationClick}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.assetInventory.addIntegration"
|
||||
defaultMessage="Add integration"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer size="l" />
|
||||
</>
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
} from '../hooks/use_asset_inventory_url_state/use_asset_inventory_url_state';
|
||||
|
||||
import { LOCAL_STORAGE_COLUMNS_KEY, LOCAL_STORAGE_DATA_TABLE_PAGE_SIZE_KEY } from '../constants';
|
||||
import { OnboardingSuccessCallout } from '../components/onboarding/onboarding_success_callout';
|
||||
|
||||
const getDefaultQuery = ({ query, filters }: AssetsBaseURLQuery): URLQuery => ({
|
||||
query,
|
||||
|
@ -53,6 +54,7 @@ export const AllAssets = () => {
|
|||
<AssetInventorySearchBar query={urlQuery} setQuery={setUrlQuery} />
|
||||
<EuiPageTemplate.Section>
|
||||
<AssetInventoryTitle />
|
||||
<OnboardingSuccessCallout />
|
||||
<AssetInventoryFilters setQuery={setUrlQuery} />
|
||||
<AssetInventoryBarChart
|
||||
isLoading={isLoadingChartData}
|
||||
|
|
|
@ -14,6 +14,7 @@ import { API_VERSIONS } from '../../../../common/constants';
|
|||
import type { AssetInventoryRoutesDeps } from '../types';
|
||||
import { InitEntityStoreRequestBody } from '../../../../common/api/entity_analytics/entity_store/enable.gen';
|
||||
import { ASSET_INVENTORY_ENABLE_API_PATH } from '../../../../common/api/asset_inventory/constants';
|
||||
import { checkAndInitAssetCriticalityResources } from '../../entity_analytics/asset_criticality/check_and_init_asset_criticality_resources';
|
||||
|
||||
export const enableAssetInventoryRoute = (
|
||||
router: AssetInventoryRoutesDeps['router'],
|
||||
|
@ -44,6 +45,9 @@ export const enableAssetInventoryRoute = (
|
|||
const secSol = await context.securitySolution;
|
||||
|
||||
try {
|
||||
// Criticality resources are required by the Entity Store transforms
|
||||
await checkAndInitAssetCriticalityResources(context, logger);
|
||||
|
||||
const body = await secSol.getAssetInventoryClient().enable(secSol, request.body);
|
||||
|
||||
return response.ok({ body });
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue