Skip to content

Commit

Permalink
[Workspace] search use case overview page (#7877)
Browse files Browse the repository at this point in the history
* search overview

Signed-off-by: Hailong Cui <[email protected]>

* add unit test for search use case overview page

Signed-off-by: Hailong Cui <[email protected]>

* Changeset file for PR #7877 created/updated

* fix merge issue

Signed-off-by: Hailong Cui <[email protected]>

* udpate breadcrumbs for workspace overview page

Signed-off-by: Hailong Cui <[email protected]>

* fix failed ut

Signed-off-by: Hailong Cui <[email protected]>

* fix merge issue

Signed-off-by: Hailong Cui <[email protected]>

* fix ut

Signed-off-by: Hailong Cui <[email protected]>

---------

Signed-off-by: Hailong Cui <[email protected]>
Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com>
Co-authored-by: Yulong Ruan <[email protected]>
(cherry picked from commit 9697f33)
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
3 people committed Sep 4, 2024
1 parent 999bc05 commit 1a44ce7
Show file tree
Hide file tree
Showing 17 changed files with 664 additions and 29 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/7877.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- [Workspace] Add search use case overview page ([#7877](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7877))
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ test('render list of cards', () => {
jest
.spyOn(embeddableStart, 'EmbeddablePanel')
.mockImplementation(() => <span>CardEmbeddablePanel</span>);
render(
const { container, queryAllByText } = render(
<CardList
embeddableServices={embeddableStart}
embeddable={
Expand All @@ -37,5 +37,37 @@ test('render list of cards', () => {
}
/>
);
expect(screen.queryAllByText('CardEmbeddablePanel')).toHaveLength(2);
expect(queryAllByText('CardEmbeddablePanel')).toHaveLength(2);
// verify container has div with class euiFlexGroup
expect(container.querySelector('.euiFlexGroup')).toBeInTheDocument();
});

test('render list of cards with grid', () => {
const embeddableStart = embeddablePluginMock.createStartContract();
jest
.spyOn(embeddableStart, 'EmbeddablePanel')
.mockImplementation(() => <span>CardEmbeddablePanel</span>);
const { container } = render(
<CardList
embeddableServices={embeddableStart}
embeddable={
new CardContainer(
{
id: 'card',
panels: {
'card-id-1': { type: CARD_EMBEDDABLE, explicitInput: { id: 'card-id-1' } },
'card-id-2': { type: CARD_EMBEDDABLE, explicitInput: { id: 'card-id-2' } },
'card-id-3': { type: CARD_EMBEDDABLE, explicitInput: { id: 'card-id-3' } },
'card-id-4': { type: CARD_EMBEDDABLE, explicitInput: { id: 'card-id-4' } },
},
grid: true,
columns: 2,
},
embeddableStart
)
}
/>
);
// verify container has div with class euiFlexGrid
expect(container.querySelector('.euiFlexGrid')).toBeInTheDocument();
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
*/

import React from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { EuiFlexGrid, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';

import { FlexGridColumns } from '@elastic/eui/src/components/flex/flex_grid';
import {
IContainer,
withEmbeddableSubscription,
Expand All @@ -22,13 +23,18 @@ interface Props {
}

const CardListInner = ({ embeddable, input, embeddableServices }: Props) => {
if (input.columns) {
if (input.columns && !input.grid) {
const width = `${(1 / input.columns) * 100}%`;
const cards = Object.values(input.panels).map((panel) => {
const child = embeddable.getChild(panel.explicitInput.id);
return (
<EuiFlexItem key={panel.explicitInput.id} style={{ minWidth: `calc(${width} - 8px)` }}>
<embeddableServices.EmbeddablePanel embeddable={child} />
<embeddableServices.EmbeddablePanel
embeddable={child}
hideHeader
hasBorder={false}
hasShadow={false}
/>
</EuiFlexItem>
);
});
Expand Down Expand Up @@ -57,6 +63,14 @@ const CardListInner = ({ embeddable, input, embeddableServices }: Props) => {
);
});

if (input.grid && input.columns) {
return (
<EuiFlexGrid columns={input.columns as FlexGridColumns} gutterSize="s">
{cards}
</EuiFlexGrid>
);
}

return (
<EuiFlexGroup wrap={input.wrap} gutterSize="s">
{cards}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface CardExplicitInput {
export type CardContainerInput = ContainerInput<CardExplicitInput> & {
columns?: number;
wrap?: boolean;
grid?: boolean;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const createCardInput = (
viewMode: ViewMode.VIEW,
columns: section.columns,
wrap: section.wrap,
grid: section.grid,
panels,
...section.input,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export type Section =
input?: CardContainerExplicitInput;
columns?: number;
wrap?: boolean;
grid?: boolean;
};

export type Content =
Expand Down
19 changes: 19 additions & 0 deletions src/plugins/home/public/application/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import { HomeApp, ImportSampleDataApp } from './components/home_app';
import { getServices } from './opensearch_dashboards_services';

import './index.scss';
import { ContentManagementPluginStart } from '../../../../plugins/content_management/public';
import { SearchUseCaseOverviewApp } from './components/usecase_overview/search_use_case_app';

export const renderApp = async (
element: HTMLElement,
Expand Down Expand Up @@ -90,3 +92,20 @@ export const renderImportSampleDataApp = async (element: HTMLElement, coreStart:
unmountComponentAtNode(element);
};
};

export const renderSearchUseCaseOverviewApp = async (
element: HTMLElement,
coreStart: CoreStart,
contentManagementStart: ContentManagementPluginStart
) => {
render(

Check warning on line 101 in src/plugins/home/public/application/application.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/home/public/application/application.tsx#L101

Added line #L101 was not covered by tests
<OpenSearchDashboardsContextProvider services={{ ...coreStart }}>
<SearchUseCaseOverviewApp contentManagement={contentManagementStart} />
</OpenSearchDashboardsContextProvider>,
element
);

return () => {
unmountComponentAtNode(element);

Check warning on line 109 in src/plugins/home/public/application/application.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/home/public/application/application.tsx#L108-L109

Added lines #L108 - L109 were not covered by tests
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('Sample data card', () => {
it('should call the getTargetArea function with the correct arguments', () => {
registerSampleDataCard(contentManagement, coreStart);
const call = registerContentProviderMock.mock.calls[0];
expect(call[0].getTargetArea()).toEqual(['essentials_overview/get_started']);
expect(call[0].getTargetArea()).toEqual('essentials_overview/get_started');
expect(call[0].getContent()).toMatchInlineSnapshot(`
Object {
"cardProps": Object {
Expand All @@ -39,5 +39,10 @@ describe('Sample data card', () => {
"title": "Try openSearch",
}
`);

// search use case overview
expect(registerContentProviderMock.mock.calls[1][0].getTargetArea()).toEqual(
'search_overview/get_started'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,23 @@ import { EuiI18n } from '@elastic/eui';
import { i18n } from '@osd/i18n';
import {
ContentManagementPluginStart,
ContentProvider,
ESSENTIAL_OVERVIEW_CONTENT_AREAS,
SEARCH_OVERVIEW_CONTENT_AREAS,
} from '../../../../../content_management/public';
import { IMPORT_SAMPLE_DATA_APP_ID } from '../../../../common/constants';

export const registerSampleDataCard = (
contentManagement: ContentManagementPluginStart,
core: CoreStart
) => {
contentManagement.registerContentProvider({
id: `get_start_sample_data`,
getTargetArea: () => [ESSENTIAL_OVERVIEW_CONTENT_AREAS.GET_STARTED],
const sampleDataCard = (order: number, targetArea: string): ContentProvider => ({
id: `get_start_sample_data_${targetArea}`,
getTargetArea: () => targetArea,
getContent: () => ({
id: 'sample_data',
kind: 'card',
order: 0,
order,
description: i18n.translate('home.sampleData.card.description', {
defaultMessage: 'You can install sample data to experiment with OpenSearch Dashboards.',
}),
Expand All @@ -42,4 +44,11 @@ export const registerSampleDataCard = (
},
}),
});

contentManagement.registerContentProvider(
sampleDataCard(0, ESSENTIAL_OVERVIEW_CONTENT_AREAS.GET_STARTED)
);
contentManagement.registerContentProvider(
sampleDataCard(30, SEARCH_OVERVIEW_CONTENT_AREAS.GET_STARTED)
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { render } from '@testing-library/react';
import React from 'react';
import { coreMock } from '../../../../../../core/public/mocks';
import { OpenSearchDashboardsContextProvider } from '../../../../../opensearch_dashboards_react/public';
import { contentManagementPluginMocks } from '../../../../../content_management/public/mocks';
import { SearchUseCaseOverviewApp } from './search_use_case_app';
import { ContentManagementPluginStart } from '../../../../../content_management/public';
import { BehaviorSubject } from 'rxjs';
import { WorkspaceObject } from 'opensearch-dashboards/public';

describe('<SearchUseCaseOverviewApp />', () => {
const renderPageMock = jest.fn();
renderPageMock.mockReturnValue('dummy page');
const mock = {
...contentManagementPluginMocks.createStartContract(),
renderPage: renderPageMock,
};
const coreStartMocks = coreMock.createStart();

function renderSearchUseCaseOverviewApp(
contentManagement: ContentManagementPluginStart,
services = { ...coreStartMocks }
) {
return (
<OpenSearchDashboardsContextProvider services={services}>
<SearchUseCaseOverviewApp contentManagement={contentManagement} />
</OpenSearchDashboardsContextProvider>
);
}

beforeEach(() => {
jest.clearAllMocks();
});

it('render for workspace disabled case', () => {
const { container } = render(renderSearchUseCaseOverviewApp(mock, coreStartMocks));

expect(container).toMatchInlineSnapshot(`
<div>
dummy page
</div>
`);

expect(coreStartMocks.chrome.setBreadcrumbs).toHaveBeenCalledWith([
{ text: 'Search overview' },
]);
expect(mock.renderPage).toBeCalledWith('search_overview');
});

it('render for workspace enabled case', () => {
const coreStartMocksWithWorkspace = {
...coreStartMocks,
workspaces: {
...coreStartMocks.workspaces,
currentWorkspace$: new BehaviorSubject({
id: 'foo',
name: 'foo ws',
}),
},
};

const { container } = render(renderSearchUseCaseOverviewApp(mock, coreStartMocksWithWorkspace));

expect(container).toMatchInlineSnapshot(`
<div>
dummy page
</div>
`);

expect(coreStartMocks.chrome.setBreadcrumbs).toHaveBeenCalledWith([{ text: 'foo ws' }]);
expect(mock.renderPage).toBeCalledWith('search_overview');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useEffect } from 'react';
import { useObservable } from 'react-use';
import { CoreStart } from 'opensearch-dashboards/public';
import { EuiBreadcrumb } from '@elastic/eui';
import { I18nProvider } from '@osd/i18n/react';
import { i18n } from '@osd/i18n';
import { useOpenSearchDashboards } from '../../../../../opensearch_dashboards_react/public';
import {
ContentManagementPluginStart,
SEARCH_OVERVIEW_PAGE_ID,
} from '../../../../../content_management/public';

interface Props {
contentManagement: ContentManagementPluginStart;
}

export const SearchUseCaseOverviewApp = ({ contentManagement }: Props) => {
const {
services: { workspaces, chrome },
} = useOpenSearchDashboards<CoreStart>();

const currentWorkspace = useObservable(workspaces.currentWorkspace$);

const title = i18n.translate('home.usecase.search.title', { defaultMessage: 'Search overview' });

useEffect(() => {
const breadcrumbs: EuiBreadcrumb[] = [
{
text: currentWorkspace?.name || title,
},
];
chrome.setBreadcrumbs(breadcrumbs);
}, [chrome, currentWorkspace, title]);

return (
<I18nProvider>
{contentManagement ? contentManagement.renderPage(SEARCH_OVERVIEW_PAGE_ID) : null}
</I18nProvider>
);
};
Loading

0 comments on commit 1a44ce7

Please sign in to comment.