[Security Solution] Replace sourcerer in global header (#216685)

## Summary

This PR replaces the Sourcerer component with the data view picker from
discover app, if the feature flag is enabled.

<img width="600" alt="Screenshot 2025-04-02 at 09 46 21"
src="https://github.com/user-attachments/assets/7ec35bf5-12dc-4e72-9292-4a734034806f"
/>

**Until we add relevant hooks on the target page that has the global
header, this will only be a cometic change.**
Don't expect data view changes made in the picker to be applied just
yet.

### Testing

Add the following feature flag to your configuration:

```xpack.securitySolution.enableExperimental: ['newDataViewPickerEnabled']```

and navigate to a page with global header, eg. alerts.

### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios

### Identify risks
We should still show the original sourcerer if the flag is not enabled
This commit is contained in:
Luke Gmys 2025-04-02 17:22:48 +02:00 committed by GitHub
parent a750c7366e
commit 26d1255edd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 58 additions and 14 deletions

View file

@ -23,6 +23,11 @@ import {
import { TimelineId } from '../../../../common/types/timeline';
import { sourcererPaths } from '../../../sourcerer/containers/sourcerer_paths';
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import { DATA_VIEW_PICKER_TEST_ID } from '../../../data_view_manager/components/data_view_picker/constants';
jest.mock('../../../common/hooks/use_experimental_features');
jest.mock('react-router-dom', () => {
const actual = jest.requireActual('react-router-dom');
return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) };
@ -156,4 +161,22 @@ describe('global header', () => {
waitFor(() => expect(findByTestId('assistantNavLink')).toBeInTheDocument());
});
describe('when new data view picker is enabled', () => {
beforeEach(() => {
// Mocking location to be alerts page
(useLocation as jest.Mock).mockReturnValue({ pathname: sourcererPaths[0] });
jest.mocked(useIsExperimentalFeatureEnabled).mockReturnValue(true);
});
it('should render it instead of sourcerer', () => {
const { queryByTestId } = render(
<TestProviders store={store}>
<GlobalHeader />
</TestProviders>
);
expect(queryByTestId('sourcerer-trigger')).not.toBeInTheDocument();
expect(queryByTestId(DATA_VIEW_PICKER_TEST_ID)).toBeInTheDocument();
});
});
});

View file

@ -16,6 +16,7 @@ import { createHtmlPortalNode, InPortal, OutPortal } from 'react-reverse-portal'
import { i18n } from '@kbn/i18n';
import { toMountPoint } from '@kbn/react-kibana-mount';
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import { MlPopover } from '../../../common/components/ml_popover/ml_popover';
import { useKibana } from '../../../common/lib/kibana';
import { isDetectionsPath, isDashboardViewPath } from '../../../helpers';
@ -29,6 +30,7 @@ import {
showSourcererByPath,
} from '../../../sourcerer/containers/sourcerer_paths';
import { useAddIntegrationsUrl } from '../../../common/hooks/use_add_integrations_url';
import { DataViewPicker } from '../../../data_view_manager/components/data_view_picker';
const BUTTON_ADD_DATA = i18n.translate('xpack.securitySolution.globalHeader.buttonAddData', {
defaultMessage: 'Add integrations',
@ -75,6 +77,14 @@ export const GlobalHeader = React.memo(() => {
}
}, [portalNode, setHeaderActionMenu, theme, kibanaServiceI18n, dashboardViewPath]);
const newDataViewPickerEnabled = useIsExperimentalFeatureEnabled('newDataViewPickerEnabled');
const dataViewPicker = newDataViewPickerEnabled ? (
<DataViewPicker scope={sourcererScope} />
) : (
<Sourcerer scope={sourcererScope} data-test-subj="sourcerer" />
);
return (
<InPortal node={portalNode}>
<EuiHeaderSection side="right">
@ -95,9 +105,7 @@ export const GlobalHeader = React.memo(() => {
>
{BUTTON_ADD_DATA}
</EuiHeaderLink>
{showSourcerer && !showTimeline && (
<Sourcerer scope={sourcererScope} data-test-subj="sourcerer" />
)}
{showSourcerer && !showTimeline && dataViewPicker}
</EuiHeaderLinks>
</EuiHeaderSectionItem>
</EuiHeaderSection>

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export const DATA_VIEW_PICKER_TEST_ID = 'new-data-view-picker' as const;

View file

@ -17,6 +17,7 @@ import { useDataViewSpec } from '../../hooks/use_data_view_spec';
import { sharedStateSelector } from '../../redux/selectors';
import { sharedDataViewManagerSlice } from '../../redux/slices';
import { useSelectDataView } from '../../hooks/use_select_data_view';
import { DATA_VIEW_PICKER_TEST_ID } from './constants';
export const DataViewPicker = memo((props: { scope: DataViewManagerScopeName }) => {
const dispatch = useDispatch();
@ -119,17 +120,19 @@ export const DataViewPicker = memo((props: { scope: DataViewManagerScopeName })
}, [adhocDataViewSpecs, fieldFormats]);
return (
<UnifiedDataViewPicker
isDisabled={status !== 'ready'}
currentDataViewId={dataViewId || DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID}
trigger={triggerConfig}
onChangeDataView={handleChangeDataView}
onEditDataView={handleDataViewModified}
onAddField={handleAddField}
onDataViewCreated={createNewDataView}
adHocDataViews={adhocDataViews}
savedDataViews={savedDataViews}
/>
<div data-test-subj={DATA_VIEW_PICKER_TEST_ID}>
<UnifiedDataViewPicker
isDisabled={status !== 'ready'}
currentDataViewId={dataViewId || DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID}
trigger={triggerConfig}
onChangeDataView={handleChangeDataView}
onEditDataView={handleDataViewModified}
onAddField={handleAddField}
onDataViewCreated={createNewDataView}
adHocDataViews={adhocDataViews}
savedDataViews={savedDataViews}
/>
</div>
);
});

View file

@ -53,6 +53,7 @@ describe('createInitListener', () => {
id: DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID,
scope: [
DataViewManagerScopeName.default,
DataViewManagerScopeName.detections,
DataViewManagerScopeName.timeline,
DataViewManagerScopeName.analyzer,
],

View file

@ -33,6 +33,7 @@ export const createInitListener = (dependencies: { dataViews: DataViewsServicePu
id: DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID,
scope: [
DataViewManagerScopeName.default,
DataViewManagerScopeName.detections,
DataViewManagerScopeName.timeline,
DataViewManagerScopeName.analyzer,
],