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 <szhongna@amazon.com>
  • Loading branch information
zhongnansu committed Apr 12, 2024
1 parent c89d304 commit 4f83c10
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 6 deletions.

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 4f83c10

Please sign in to comment.