Skip to content

Commit

Permalink
Merge branch 'main' into refactor-workspace-creation-details-panel
Browse files Browse the repository at this point in the history
  • Loading branch information
wanglam authored Sep 29, 2024
2 parents 4bd41a2 + c1a349a commit b714df6
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 53 deletions.
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: 2 additions & 0 deletions changelogs/fragments/8315.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fix:
- Hide delete button for non osd admins in workspace list ([#8315](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8315))
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 @@ -217,10 +217,17 @@ export class SampleDataSetCards extends React.Component {

render() {
return (
<EuiFlexGrid columns={3} className="homSampleDataSetCards">
<EuiFlexGrid
columns={3}
className="homSampleDataSetCards"
gutterSize={this.props.useUpdatedUX ? 'm' : undefined}
>
{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 +254,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 {
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 homeTitle = i18n.translate('home.breadcrumbs.homeTitle', { defaultMessage:
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 @@ class TutorialDirectoryUi extends React.Component {
notices: getServices().tutorialService.getDirectoryNotices(),
isDataSourceEnabled: !!getServices().dataSource,
isLocalClusterHidden: getServices().dataSource?.hideLocalCluster ?? false,
useUpdatedUX: getServices().uiSettings.get('home:useNewHomePage'),
};
}

Expand All @@ -95,7 +103,7 @@ class TutorialDirectoryUi extends React.Component {
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 @@ class TutorialDirectoryUi extends React.Component {
dataSourceId={this.state.selectedDataSourceId}
isDataSourceEnabled={this.state.isDataSourceEnabled}
isLocalClusterHidden={this.state.isLocalClusterHidden}
useUpdatedUX={this.state.useUpdatedUX}
/>
);
}
Expand Down Expand Up @@ -229,21 +238,43 @@ class TutorialDirectoryUi extends React.Component {
};

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

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

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 @@ class TutorialDirectoryUi extends React.Component {
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 @@ class TutorialDirectoryUi extends React.Component {
};

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 @@ TutorialDirectoryUi.propTypes = {
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

0 comments on commit b714df6

Please sign in to comment.