Skip to content

Commit

Permalink
[MD] Add dropdown header to Data source single selector
Browse files Browse the repository at this point in the history
Signed-off-by: Zhongnan Su <[email protected]>
  • Loading branch information
zhongnansu committed Apr 12, 2024
1 parent c89d304 commit 8b21cb1
Show file tree
Hide file tree
Showing 13 changed files with 238 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- [Dynamic Configurations] Improve dynamic configurations by adding cache and simplifying client fetch ([#6364](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6364))
- [MD] Add OpenSearch cluster group label to top of single selectable dropdown ([#6400](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6400))
- [Workspace] Support workspace in saved objects client in server side. ([#6365](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6365))
- [MD] Add dropdown header to data source single selector ([#6431](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6431))

### 🐛 Bug Fixes

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import { DataSourcePluginSetup } from 'src/plugins/data_source/public';
import { DataSourceMenu } from './data_source_menu';
import { DataSourceMenuProps } from './types';
import { MountPointPortal } from '../../../../opensearch_dashboards_react/public';
import { applicationService } from '../utils';

export function createDataSourceMenu<T>(
uiSettings: IUiSettingsClient,
dataSourcePluginSetup: DataSourcePluginSetup
) {
const application = applicationService.getApplication();
return (props: DataSourceMenuProps<T>) => {
const { hideLocalCluster } = dataSourcePluginSetup;
if (props.setMenuMountPoint) {
Expand All @@ -25,13 +27,19 @@ export function createDataSourceMenu<T>(
{...props}
uiSettings={uiSettings}
hideLocalCluster={hideLocalCluster}
application={application}
/>
</EuiHeaderLinks>
</MountPointPortal>
);
}
return (
<DataSourceMenu {...props} uiSettings={uiSettings} hideLocalCluster={hideLocalCluster} />
<DataSourceMenu
{...props}
uiSettings={uiSettings}
hideLocalCluster={hideLocalCluster}
application={application}
/>
);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ import {
import { DataSourceSelectable } from '../data_source_selectable';

export function DataSourceMenu<T>(props: DataSourceMenuProps<T>): ReactElement | null {
const { componentType, componentConfig, uiSettings, hideLocalCluster } = props;

const { componentType, componentConfig, uiSettings, hideLocalCluster, application } = props;
function renderDataSourceView(config: DataSourceViewConfig): ReactElement | null {
const { activeOption, fullWidth, savedObjects, notifications } = config;
return (
Expand Down Expand Up @@ -70,6 +69,7 @@ export function DataSourceMenu<T>(props: DataSourceMenuProps<T>): ReactElement |
hideLocalCluster={hideLocalCluster || false}
fullWidth={fullWidth}
uiSettings={uiSettings}
application={application}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
SavedObjectsClientContract,
SavedObject,
IUiSettingsClient,
ApplicationStart,
} from '../../../../../core/public';
import { DataSourceAttributes } from '../../types';

Expand All @@ -31,6 +32,7 @@ export interface DataSourceMenuProps<T = any> {
componentConfig: T;
hideLocalCluster?: boolean;
uiSettings?: IUiSettingsClient;
application?: ApplicationStart;
setMenuMountPoint?: (menuMount: MountPoint | undefined) => void;
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -431,4 +431,50 @@ describe('DataSourceSelectable', () => {
const optionsProp = component.find(EuiSelectable).prop('options');
expect(optionsProp).toEqual([]);
});

it('should render group lablel normally after onChange', async () => {
const onSelectedDataSource = jest.fn();
component = shallow(
<DataSourceSelectable
savedObjectsClient={client}
notifications={toasts}
onSelectedDataSources={onSelectedDataSource}
disabled={false}
hideLocalCluster={true}
fullWidth={false}
selectedOption={[{ id: 'test1', label: 'test1' }]}
/>
);
const componentInstance = component.instance();

componentInstance.componentDidMount!();
await nextTick();
const optionsPropBefore = component.find(EuiSelectable).prop('options');
expect(optionsPropBefore).toEqual([
opensearchClusterGroupLabel,
{
id: 'test1',
label: 'test1',
checked: 'on',
},
{
id: 'test2',
label: 'test2',
},
{
id: 'test3',
label: 'test3',
},
]);
componentInstance.onChange([
opensearchClusterGroupLabel,
{ id: 'test2', label: 'test2', checked: 'on' },
]);
await nextTick();
const optionsPropAfter = component.find(EuiSelectable).prop('options');
expect(optionsPropAfter).toEqual([
opensearchClusterGroupLabel,
{ id: 'test2', label: 'test2', checked: 'on' },
]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiBadge,
EuiHorizontalRule,
} from '@elastic/eui';
import {
ApplicationStart,
IUiSettingsClient,
SavedObjectsClientContract,
ToastsStart,
Expand All @@ -26,6 +28,7 @@ import { LocalCluster } from '../data_source_selector/data_source_selector';
import { SavedObject } from '../../../../../core/public';
import { DataSourceAttributes } from '../../types';
import { DataSourceGroupLabelOption, DataSourceOption } from '../data_source_menu/types';
import { DataSourceDropDownHeader } from '../drop_down_header';

interface DataSourceSelectableProps {
savedObjectsClient: SavedObjectsClientContract;
Expand All @@ -34,6 +37,7 @@ interface DataSourceSelectableProps {
disabled: boolean;
hideLocalCluster: boolean;
fullWidth: boolean;
application?: ApplicationStart;
selectedOption?: DataSourceOption[];
dataSourceFilter?: (dataSource: SavedObject<DataSourceAttributes>) => boolean;
uiSettings?: IUiSettingsClient;
Expand Down Expand Up @@ -198,6 +202,7 @@ export class DataSourceSelectable extends React.Component<

onChange(options: SelectedDataSourceOption[]) {
if (!this._isMounted) return;
options = options.filter((option) => !option.hasOwnProperty('isGroupLabel'));
const selectedDataSource = options.find(({ checked }) => checked);

this.setState({ dataSourceOptions: options });
Expand Down Expand Up @@ -247,6 +252,7 @@ export class DataSourceSelectable extends React.Component<

return (
<EuiPopover
initialFocus={'.euiSelectableSearch'}
id={'dataSourceSelectableContextMenuPopover'}
button={button}
isOpen={this.state.isPopoverOpen}
Expand All @@ -257,6 +263,11 @@ export class DataSourceSelectable extends React.Component<
>
<EuiContextMenuPanel>
<EuiPanel color="transparent" paddingSize="s" style={{ width: '300px' }}>
<DataSourceDropDownHeader
dataSourceCount={this.state.dataSourceOptions.length}
application={this.props.application}
/>
<EuiHorizontalRule margin="none" />
<EuiSpacer size="s" />
<EuiSelectable
aria-label="Search"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
mockResponseForSavedObjectsCalls,
} from '../../mocks';
import { AuthType } from 'src/plugins/data_source/common/data_sources';
import * as utils from '../utils';
import { EuiComboBox } from '@elastic/eui';

describe('DataSourceSelector', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { shallow } from 'enzyme';
import { DataSourceDropDownHeader } from './drop_down_header';
import { coreMock } from '../../../../../src/core/public/mocks';
import { DSM_APP_ID } from '../plugin';

describe('DataSourceDropDownHeader', () => {
it('should render correctly with the provided dataSourceCount', () => {
const dataSourceCount = 5;
const wrapper = shallow(<DataSourceDropDownHeader dataSourceCount={dataSourceCount} />);
expect(wrapper).toMatchSnapshot();
});

it('should call application.navigateToApp when the "Manage" link is clicked', () => {
const dataSourceCount = 5;
const navigateToAppMock = jest.fn();
const applicationMock = coreMock.createStart().application;

const wrapper = shallow(
<DataSourceDropDownHeader dataSourceCount={dataSourceCount} application={applicationMock} />
);
wrapper.find('EuiLink').simulate('click');
expect(navigateToAppMock).toHaveBeenCalledWith('management', {
path: `opensearch-dashboards/${DSM_APP_ID}`,
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui';
import React from 'react';
import { ApplicationStart } from 'opensearch-dashboards/public';
import { DSM_APP_ID } from '../plugin';

interface DataSourceOptionItemProps {
dataSourceCount: number;
application?: ApplicationStart;
}

export const DataSourceDropDownHeader: React.FC<DataSourceOptionItemProps> = ({
dataSourceCount,
application,
}) => {
return (
<EuiTitle size="xxxs">
<EuiFlexGroup responsive={false}>
<EuiFlexItem>DATA SOURCE({dataSourceCount})</EuiFlexItem>
<div tabIndex={0} style={{ opacity: 0 }} />
<EuiFlexItem grow={false}>
<EuiLink
onClick={() =>
application?.navigateToApp('management', {
path: `opensearch-dashboards/${DSM_APP_ID}`,
})
}
>
Manage
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
</EuiTitle>
);
};
Loading

0 comments on commit 8b21cb1

Please sign in to comment.