[Search] Project Navigation - Enterprise Search support (#186429)

## Summary

Adds App Search nav items support to the new Search Project Navigation


![image](5268384a-ad1a-494b-9dbf-1963a73b86e3)

Adds Workplace Search navi items support to new Search Project
Navigation


![image](d79386e8-84f6-4e0c-9123-bb20b6d80fdc)

### Testing

Since the project navigation is currently gated in Kibana use the
following config settings in `kibana.dev.yml` to test and develop this
feature. All Changes should be test with this feature on and off.

```yaml
xpack.cloud.id: "ftr_fake_cloud_id:aGVsbG8uY29tOjQ0MyRFUzEyM2FiYyRrYm4xMjNhYmM="
xpack.cloud.base_url: "https://cloud.elastic.co"
xpack.cloud.deployment_url: "/deployments/deploymentId"

xpack.cloud_integrations.experiments.enabled: true
xpack.cloud_integrations.experiments.flag_overrides:
  "solutionNavEnabled": true
```
The enable the search nav via a space with this dev console command:
```
POST kbn:/api/spaces/space
{
  "name": "search space",
  "id": "search-space",
  "description": "a description",
  "color": "#5c5959",
  "solution": "es", 
  "disabledFeatures": []
}
```

### Checklist

- [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
This commit is contained in:
Rodney Norris 2024-07-01 13:46:47 -05:00 committed by GitHub
parent 91b59bfc2e
commit 804478bf7a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 83 additions and 8 deletions

View file

@ -8,10 +8,13 @@
jest.mock('./nav', () => ({
useAppSearchNav: () => [],
}));
import '../../../__mocks__/shallow_useeffect.mock';
import { setMockValues } from '../../../__mocks__/kea_logic';
import React from 'react';
import { shallow } from 'enzyme';
import { of } from 'rxjs';
import { SetAppSearchChrome } from '../../../shared/kibana_chrome';
import { EnterpriseSearchPageTemplateWrapper } from '../../../shared/layout';
@ -19,7 +22,16 @@ import { SendAppSearchTelemetry } from '../../../shared/telemetry';
import { AppSearchPageTemplate } from './page_template';
const mockValues = {
getChromeStyle$: () => of('classic'),
updateSideNavDefinition: jest.fn(),
};
describe('AppSearchPageTemplate', () => {
beforeEach(() => {
setMockValues({ ...mockValues });
});
it('renders', () => {
const wrapper = shallow(
<AppSearchPageTemplate>

View file

@ -7,7 +7,11 @@
import React from 'react';
import { useValues } from 'kea';
import useObservable from 'react-use/lib/useObservable';
import { APP_SEARCH_PLUGIN } from '../../../../../common/constants';
import { KibanaLogic } from '../../../shared/kibana';
import { SetAppSearchChrome } from '../../../shared/kibana_chrome';
import { EnterpriseSearchPageTemplateWrapper, PageTemplateProps } from '../../../shared/layout';
import { SendAppSearchTelemetry } from '../../../shared/telemetry';
@ -17,12 +21,27 @@ import { useAppSearchNav } from './nav';
export const AppSearchPageTemplate: React.FC<
Omit<PageTemplateProps, 'useEndpointHeaderActions'>
> = ({ children, pageChrome, pageViewTelemetry, ...pageTemplateProps }) => {
const navItems = useAppSearchNav();
const { getChromeStyle$, updateSideNavDefinition } = useValues(KibanaLogic);
const chromeStyle = useObservable(getChromeStyle$(), 'classic');
React.useEffect(() => {
if (chromeStyle === 'classic') return;
// We update the new side nav definition with the selected app items
updateSideNavDefinition({ appSearch: navItems?.[0]?.items });
}, [chromeStyle, navItems, updateSideNavDefinition]);
React.useEffect(() => {
return () => {
updateSideNavDefinition({ appSearch: undefined });
};
}, [updateSideNavDefinition]);
return (
<EnterpriseSearchPageTemplateWrapper
{...pageTemplateProps}
solutionNav={{
name: APP_SEARCH_PLUGIN.NAME,
items: useAppSearchNav(),
items: chromeStyle === 'classic' ? navItems : undefined,
}}
setPageChrome={pageChrome && <SetAppSearchChrome trail={pageChrome} />}
useEndpointHeaderActions={false}

View file

@ -8,10 +8,13 @@
jest.mock('./nav', () => ({
useWorkplaceSearchNav: () => [],
}));
import '../../../__mocks__/shallow_useeffect.mock';
import { setMockValues } from '../../../__mocks__/kea_logic';
import React from 'react';
import { shallow } from 'enzyme';
import { of } from 'rxjs';
import { SetWorkplaceSearchChrome } from '../../../shared/kibana_chrome';
import { EnterpriseSearchPageTemplateWrapper } from '../../../shared/layout';
@ -19,7 +22,16 @@ import { SendWorkplaceSearchTelemetry } from '../../../shared/telemetry';
import { WorkplaceSearchPageTemplate } from './page_template';
const mockValues = {
getChromeStyle$: () => of('classic'),
updateSideNavDefinition: jest.fn(),
};
describe('WorkplaceSearchPageTemplate', () => {
beforeEach(() => {
setMockValues({ ...mockValues });
});
it('renders', () => {
const wrapper = shallow(
<WorkplaceSearchPageTemplate>

View file

@ -7,7 +7,11 @@
import React from 'react';
import { useValues } from 'kea';
import useObservable from 'react-use/lib/useObservable';
import { WORKPLACE_SEARCH_PLUGIN } from '../../../../../common/constants';
import { KibanaLogic } from '../../../shared/kibana';
import { SetWorkplaceSearchChrome } from '../../../shared/kibana_chrome';
import { EnterpriseSearchPageTemplateWrapper, PageTemplateProps } from '../../../shared/layout';
import { SendWorkplaceSearchTelemetry } from '../../../shared/telemetry';
@ -20,13 +24,28 @@ export const WorkplaceSearchPageTemplate: React.FC<PageTemplateProps> = ({
pageViewTelemetry,
...pageTemplateProps
}) => {
const navItems = useWorkplaceSearchNav();
const { getChromeStyle$, updateSideNavDefinition } = useValues(KibanaLogic);
const chromeStyle = useObservable(getChromeStyle$(), 'classic');
React.useEffect(() => {
if (chromeStyle === 'classic') return;
// We update the new side nav definition with the selected app items
updateSideNavDefinition({ workplaceSearch: navItems?.[0]?.items });
}, [chromeStyle, navItems, updateSideNavDefinition]);
React.useEffect(() => {
return () => {
updateSideNavDefinition({ workplaceSearch: undefined });
};
}, [updateSideNavDefinition]);
return (
<EnterpriseSearchPageTemplateWrapper
restrictWidth
{...pageTemplateProps}
solutionNav={{
items: chromeStyle === 'classic' ? navItems : undefined,
name: WORKPLACE_SEARCH_PLUGIN.NAME,
items: useWorkplaceSearchNav(),
}}
setPageChrome={pageChrome && <SetWorkplaceSearchChrome trail={pageChrome} />}
useEndpointHeaderActions={false}

View file

@ -21,9 +21,11 @@ import { SEARCH_APPLICATIONS_PATH } from './applications/applications/routes';
import { SEARCH_INDICES_PATH } from './applications/enterprise_search_content/routes';
export interface DynamicSideNavItems {
appSearch?: Array<EuiSideNavItemType<unknown>>;
collections?: Array<EuiSideNavItemType<unknown>>;
indices?: Array<EuiSideNavItemType<unknown>>;
searchApps?: Array<EuiSideNavItemType<unknown>>;
workplaceSearch?: Array<EuiSideNavItemType<unknown>>;
}
const title = i18n.translate(
@ -79,7 +81,7 @@ export const getNavigationTreeDefinition = ({
id: 'es',
navigationTree$: dynamicItems$.pipe(
debounceTime(10),
map(({ indices, searchApps, collections }) => {
map(({ appSearch, indices, searchApps, collections, workplaceSearch }) => {
const navTree: NavigationTreeDefinition = {
body: [
{
@ -218,11 +220,7 @@ export const getNavigationTreeDefinition = ({
{
children: [
{
getIsActive: ({ pathNameSerialized, prepend }) => {
return pathNameSerialized.startsWith(
prepend('/app/enterprise_search/app_search')
);
},
getIsActive: () => false,
link: 'appSearch:engines',
title: i18n.translate(
'xpack.enterpriseSearch.searchNav.entsearch.appSearch',
@ -230,9 +228,24 @@ export const getNavigationTreeDefinition = ({
defaultMessage: 'App Search',
}
),
...(appSearch
? {
children: appSearch.map(euiItemTypeToNodeDefinition),
isCollapsible: false,
renderAs: 'accordion',
}
: {}),
},
{
getIsActive: () => false,
link: 'workplaceSearch',
...(workplaceSearch
? {
children: workplaceSearch.map(euiItemTypeToNodeDefinition),
isCollapsible: false,
renderAs: 'accordion',
}
: {}),
},
],
id: 'entsearch',