Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update sample data page UI when useUpdatedUX enabled #8291

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/8291.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- Update sample data page UI when useUpdatedUX enabled ([#8291](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8291))
2 changes: 1 addition & 1 deletion src/plugins/home/opensearch_dashboards.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "opensearchDashboards",
"server": true,
"ui": true,
"requiredPlugins": ["data", "urlForwarding", "savedObjects", "contentManagement"],
"requiredPlugins": ["data", "urlForwarding", "savedObjects", "contentManagement", "navigation"],
"optionalPlugins": ["usageCollection", "telemetry", "dataSource"],
"requiredBundles": ["opensearchDashboardsReact", "dataSourceManagement"]
}
20 changes: 15 additions & 5 deletions src/plugins/home/public/application/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { i18n } from '@osd/i18n';
import { ScopedHistory, CoreStart } from 'opensearch-dashboards/public';
import { ScopedHistory, CoreStart, MountPoint } from 'opensearch-dashboards/public';
import { OpenSearchDashboardsContextProvider } from '../../../opensearch_dashboards_react/public';
import { NavigationPublicPluginStart } from '../../../navigation/public';
// @ts-ignore
import { HomeApp, ImportSampleDataApp } from './components/home_app';
import { getServices } from './opensearch_dashboards_services';
Expand All @@ -43,7 +44,10 @@ import { SearchUseCaseOverviewApp } from './components/usecase_overview/search_u

export const renderApp = async (
element: HTMLElement,
coreStart: CoreStart,
startServices: CoreStart & {
navigation: NavigationPublicPluginStart;
setHeaderActionMenu: (menuMount: MountPoint | undefined) => void;
},
history: ScopedHistory
) => {
const homeTitle = i18n.translate('home.breadcrumbs.homeTitle', { defaultMessage: 'Home' });
Expand All @@ -68,7 +72,7 @@ export const renderApp = async (
});

render(
<OpenSearchDashboardsContextProvider services={{ ...coreStart }}>
<OpenSearchDashboardsContextProvider services={startServices}>
<HomeApp directories={directories} solutions={solutions} />
</OpenSearchDashboardsContextProvider>,
element
Expand All @@ -80,9 +84,15 @@ export const renderApp = async (
};
};

export const renderImportSampleDataApp = async (element: HTMLElement, coreStart: CoreStart) => {
export const renderImportSampleDataApp = async (
element: HTMLElement,
startServices: CoreStart & {
navigation: NavigationPublicPluginStart;
setHeaderActionMenu: (menuMount: MountPoint | undefined) => void;
}
) => {
render(
<OpenSearchDashboardsContextProvider services={{ ...coreStart }}>
<OpenSearchDashboardsContextProvider services={startServices}>
<ImportSampleDataApp />
</OpenSearchDashboardsContextProvider>,
element
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,10 @@ export class SampleDataSetCards extends React.Component {
<EuiFlexGrid columns={3} className="homSampleDataSetCards">
{this.state.sampleDataSets.map((sampleDataSet) => {
return (
<EuiFlexItem key={sampleDataSet.id}>
<EuiFlexItem
key={sampleDataSet.id}
style={this.props.useUpdatedUX ? { maxWidth: 300 } : undefined}
>
<SampleDataSetCard
id={sampleDataSet.id}
description={sampleDataSet.description}
Expand All @@ -247,4 +250,5 @@ export class SampleDataSetCards extends React.Component {

SampleDataSetCards.propTypes = {
addBasePath: PropTypes.func.isRequired,
useUpdatedUX: PropTypes.bool,
};
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@
import { getTutorials } from '../load_tutorials';
import { injectI18n, FormattedMessage } from '@osd/i18n/react';
import { i18n } from '@osd/i18n';
import { DataSourceSelector } from '../../../../data_source_management/public';
import { DataSourceSelector, DataSourceMenu } from '../../../../data_source_management/public';
import {
MountPointPortal,
withOpenSearchDashboards,
} from '../../../../opensearch_dashboards_react/public';

const ALL_TAB_ID = 'all';
const SAMPLE_DATA_TAB_ID = 'sampleData';
Expand All @@ -60,6 +64,9 @@
const addDataTitle = i18n.translate('home.breadcrumbs.addDataTitle', {
defaultMessage: 'Add data',
});
const sampleDataTitle = i18n.translate('home.breadcrumbs.sampleDataTitle', {
defaultMessage: 'Sample data',
});

class TutorialDirectoryUi extends React.Component {
constructor(props) {
Expand All @@ -84,6 +91,7 @@
notices: getServices().tutorialService.getDirectoryNotices(),
isDataSourceEnabled: !!getServices().dataSource,
isLocalClusterHidden: getServices().dataSource?.hideLocalCluster ?? false,
useUpdatedUX: getServices().uiSettings.get('home:useNewHomePage'),
};
}

Expand All @@ -95,7 +103,7 @@
this._isMounted = true;
const { chrome } = getServices();
const { withoutHomeBreadCrumb } = this.props;
const breadcrumbs = [{ text: addDataTitle }];
const breadcrumbs = [{ text: this.state.useUpdatedUX ? sampleDataTitle : addDataTitle }];
if (!withoutHomeBreadCrumb) {
breadcrumbs.splice(0, 0, {
text: homeTitle,
Expand Down Expand Up @@ -190,6 +198,7 @@
dataSourceId={this.state.selectedDataSourceId}
isDataSourceEnabled={this.state.isDataSourceEnabled}
isLocalClusterHidden={this.state.isLocalClusterHidden}
useUpdatedUX={this.state.useUpdatedUX}
/>
);
}
Expand Down Expand Up @@ -229,21 +238,43 @@
};

renderDataSourceSelector = () => {
const { isDataSourceEnabled, isLocalClusterHidden } = this.state;
const { isDataSourceEnabled, isLocalClusterHidden, useUpdatedUX } = this.state;
const { toastNotifications, savedObjectsClient, application, uiSettings } = getServices();

return isDataSourceEnabled ? (
if (!isDataSourceEnabled) {
return null;

Check warning on line 245 in src/plugins/home/public/application/components/tutorial_directory.js

View check run for this annotation

Codecov / codecov/patch

src/plugins/home/public/application/components/tutorial_directory.js#L245

Added line #L245 was not covered by tests
}

if (useUpdatedUX) {
return (
<MountPointPortal
setMountPoint={this.props.opensearchDashboards.services.setHeaderActionMenu}
>
<DataSourceMenu
componentType="DataSourceSelectable"
componentConfig={{
notifications: toastNotifications,
savedObjects: savedObjectsClient,
onSelectedDataSources: this.onSelectedDataSourceChange,
}}
application={application}
/>
</MountPointPortal>
);
}
return (
<div className="sampleDataSourceSelector">
<DataSourceSelector
savedObjectsClient={getServices().savedObjectsClient}
notifications={getServices().toastNotifications}
savedObjectsClient={savedObjectsClient}
notifications={toastNotifications}
onSelectedDataSource={this.onSelectedDataSourceChange}
disabled={!isDataSourceEnabled}
hideLocalCluster={isLocalClusterHidden}
uiSettings={getServices().uiSettings}
uiSettings={uiSettings}
compressed={true}
/>
</div>
) : null;
);
};

renderNotices = () => {
Expand Down Expand Up @@ -275,6 +306,28 @@
renderHeader = () => {
const notices = this.renderNotices();
const headerLinks = this.renderHeaderLinks();
const { application } = getServices();
const {
navigation: {
ui: { HeaderControl },
},
} = this.props.opensearchDashboards.services;

if (this.state.useUpdatedUX) {
return (
<HeaderControl
controls={[
{
description: this.props.intl.formatMessage({
id: 'home.tutorial.card.sampleDataDescription',
defaultMessage: 'Explore sample data, visualizations, and dashboards.',
}),
},
]}
setMountPoint={application.setAppDescriptionControls}
/>
);
}

return (
<>
Expand All @@ -297,21 +350,29 @@
};

renderPageBody = () => {
const { useUpdatedUX } = this.state;
return (
<EuiPageBody component="main">
{this.renderHeader()}
<EuiSpacer size="m" />
{!useUpdatedUX && <EuiSpacer size="m" />}
{this.renderDataSourceSelector()}
<EuiTabs size="s">{this.renderTabs()}</EuiTabs>
<EuiSpacer />
<EuiSpacer size={useUpdatedUX ? 's' : undefined} />
{this.renderTabContent()}
</EuiPageBody>
);
};

render() {
const { isDataSourceEnabled } = this.state;
const { isDataSourceEnabled, useUpdatedUX } = this.state;

if (useUpdatedUX) {
return (
<EuiPage>
<EuiPanel paddingSize="m">{this.renderPageBody()}</EuiPanel>
</EuiPage>
);
}
return isDataSourceEnabled ? (
<EuiPanel paddingSize={'l'} style={{ width: '70%', margin: '50px auto' }}>
{this.renderPageBody()}
Expand All @@ -327,6 +388,7 @@
openTab: PropTypes.string,
isCloudEnabled: PropTypes.bool.isRequired,
withoutHomeBreadCrumb: PropTypes.bool,
openSearchDashboards: PropTypes.object,
};

export const TutorialDirectory = injectI18n(TutorialDirectoryUi);
export const TutorialDirectory = injectI18n(withOpenSearchDashboards(TutorialDirectoryUi));
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { setServices } from '../opensearch_dashboards_services';
import { getMockedServices } from '../opensearch_dashboards_services.mock';
import * as utils from '../../../../../plugins/data_source_management/public/components/utils';
import { DataSourceSelectionService } from '../../../../../plugins/data_source_management/public';
import { OpenSearchDashboardsContextProvider } from '../../../../opensearch_dashboards_react/public';

const makeProps = () => {
const coreMocks = coreMock.createStart();
Expand All @@ -21,24 +22,50 @@ const makeProps = () => {
};
};

const setup = async ({ props, services }) => {
const mockHeaderControl = ({ controls }) => {
return controls?.[0].description ?? controls?.[0].renderComponent ?? null;
};
const setHeaderActionMenuMock = jest.fn();

// @ts-ignore
const { TutorialDirectory } = await import('./tutorial_directory');
const finalServices = {
...services,
notifications: services.toastNotifications,
setHeaderActionMenu: services.setHeaderActionMenu ?? setHeaderActionMenuMock,
navigation: services.navigation ?? {
ui: {
HeaderControl: mockHeaderControl,
},
},
};

const renderResult = render(
<IntlProvider locale="en">
<OpenSearchDashboardsContextProvider services={finalServices}>
<TutorialDirectory {...makeProps()} {...props} />
</OpenSearchDashboardsContextProvider>
</IntlProvider>
);

return {
renderResult,
setHeaderActionMenuMock,
};
};

describe('<TutorialDirectory />', () => {
let currentService: ReturnType<typeof getMockedServices>;
beforeEach(() => {
currentService = getMockedServices();
setServices(currentService);
});
it('should render home breadcrumbs when withoutHomeBreadCrumb is undefined', async () => {
const finalProps = makeProps();
currentService.http.get.mockResolvedValueOnce([]);
spyOn(utils, 'getDataSourceSelection').and.returnValue(new DataSourceSelectionService());

// @ts-ignore
const { TutorialDirectory } = await import('./tutorial_directory');
render(
<IntlProvider locale="en">
<TutorialDirectory {...finalProps} />
</IntlProvider>
);
await setup({ services: currentService });
expect(currentService.chrome.setBreadcrumbs).toBeCalledWith([
{
href: '#/',
Expand All @@ -51,21 +78,48 @@ describe('<TutorialDirectory />', () => {
});

it('should not render home breadcrumbs when withoutHomeBreadCrumb is true', async () => {
const finalProps = makeProps();
currentService.http.get.mockResolvedValueOnce([]);
spyOn(utils, 'getDataSourceSelection').and.returnValue(new DataSourceSelectionService());

// @ts-ignore
const { TutorialDirectory } = await import('./tutorial_directory');
render(
<IntlProvider locale="en">
<TutorialDirectory {...finalProps} withoutHomeBreadCrumb />
</IntlProvider>
);
await setup({
props: { withoutHomeBreadCrumb: true },
services: currentService,
});
expect(currentService.chrome.setBreadcrumbs).toBeCalledWith([
{
text: 'Add data',
},
]);
});

it('should call setBreadcrumbs with "Sample data" when usedUpdatedUX', async () => {
currentService.http.get.mockResolvedValueOnce([]);
currentService.uiSettings.get.mockResolvedValueOnce(true);
spyOn(utils, 'getDataSourceSelection').and.returnValue(new DataSourceSelectionService());

await setup({
props: { withoutHomeBreadCrumb: true },
services: currentService,
});
expect(currentService.chrome.setBreadcrumbs).toBeCalledWith([
{
text: 'Sample data',
},
]);
});

it('should render description and call setHeaderActionMenu when usedUpdatedUX', async () => {
currentService.http.get.mockResolvedValueOnce([]);
currentService.uiSettings.get.mockResolvedValueOnce(true);
spyOn(utils, 'getDataSourceSelection').and.returnValue(new DataSourceSelectionService());

const { setHeaderActionMenuMock, renderResult } = await setup({
props: { withoutHomeBreadCrumb: true },
services: currentService,
});
expect(
renderResult.getByText('Explore sample data, visualizations, and dashboards.')
).toBeInTheDocument();
expect(setHeaderActionMenuMock).toHaveBeenCalled();
});
});
Loading
Loading