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

Refactor search bar & filters to conditionally render new look with application header #7687

Merged
merged 4 commits into from
Aug 15, 2024
Merged
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/7687.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
refactor:
- Refactor search bar & filters to conditionally render new look with application header ([#7687](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7687))
1 change: 1 addition & 0 deletions src/plugins/data/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,5 @@ export const UI_SETTINGS = {
QUERY_ENHANCEMENTS_ENABLED: 'query:enhancements:enabled',
QUERY_DATAFRAME_HYDRATION_STRATEGY: 'query:dataframe:hydrationStrategy',
SEARCH_QUERY_LANGUAGE_BLOCKLIST: 'search:queryLanguageBlocklist',
NEW_HOME_PAGE: 'home:useNewHomePage',
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,12 @@
margin-top: $euiSize * -1;
}
}

.globalFilterGroup__removeAllFilters {
zhongnansu marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please create an issue on OUI to allow setting the color in OuiContextItem?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

color: $euiColorDangerText;
}

.globalFilterGroup__filterPrefix {
padding-top: $euiSizeS;
padding-right: $euiSizeS;
}
74 changes: 19 additions & 55 deletions src/plugins/data/public/ui/filter_bar/filter_bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
EuiFlexItem,
EuiPopover,
EuiResizeObserver,
EuiText,
} from '@elastic/eui';
import { FormattedMessage, InjectedIntl, injectI18n } from '@osd/i18n/react';
import classNames from 'classnames';
Expand All @@ -43,20 +44,10 @@ import { stringify } from '@osd/std';

import { FilterEditor } from './filter_editor';
import { FilterItem } from './filter_item';
import { FilterOptions } from './filter_options';
import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';
import { IIndexPattern } from '../..';
import {
buildEmptyFilter,
Filter,
enableFilter,
disableFilter,
pinFilter,
toggleFilterDisabled,
toggleFilterNegated,
unpinFilter,
UI_SETTINGS,
} from '../../../common';
import { buildEmptyFilter, Filter, UI_SETTINGS } from '../../../common';
import { FilterOptions } from './filter_options';

interface Props {
filters: Filter[];
Expand All @@ -74,6 +65,7 @@ function FilterBarUI(props: Props) {
const [filterWidth, setFilterWidth] = useState(maxFilterWidth);

const uiSettings = opensearchDashboards.services.uiSettings;
const useNewHeader = Boolean(uiSettings!.get(UI_SETTINGS.NEW_HOME_PAGE));
if (!uiSettings) return null;

function onFiltersUpdated(filters: Filter[]) {
Expand Down Expand Up @@ -177,41 +169,10 @@ function FilterBarUI(props: Props) {
onFiltersUpdated(filters);
}

function onEnableAll() {
const filters = props.filters.map(enableFilter);
onFiltersUpdated(filters);
}

function onDisableAll() {
const filters = props.filters.map(disableFilter);
onFiltersUpdated(filters);
}

function onPinAll() {
const filters = props.filters.map(pinFilter);
onFiltersUpdated(filters);
}

function onUnpinAll() {
const filters = props.filters.map(unpinFilter);
onFiltersUpdated(filters);
}

function onToggleAllNegated() {
const filters = props.filters.map(toggleFilterNegated);
onFiltersUpdated(filters);
}

function onToggleAllDisabled() {
const filters = props.filters.map(toggleFilterDisabled);
onFiltersUpdated(filters);
}

function onRemoveAll() {
onFiltersUpdated([]);
}

const classes = classNames('globalFilterBar', props.className);
const filterBarPrefixText = i18n.translate('data.search.filterBar.filterBarPrefixText', {
defaultMessage: 'Filters: ',
});

return (
<EuiFlexGroup
Expand All @@ -221,15 +182,18 @@ function FilterBarUI(props: Props) {
responsive={false}
>
<EuiFlexItem className="globalFilterGroup__branch" grow={false}>
<FilterOptions
onEnableAll={onEnableAll}
onDisableAll={onDisableAll}
onPinAll={onPinAll}
onUnpinAll={onUnpinAll}
onToggleAllNegated={onToggleAllNegated}
onToggleAllDisabled={onToggleAllDisabled}
onRemoveAll={onRemoveAll}
/>
{useNewHeader ? (
<EuiText size="s" className="globalFilterGroup__filterPrefix">
{filterBarPrefixText}:
</EuiText>
) : (
<FilterOptions
filters={props.filters!}
onFiltersUpdated={props.onFiltersUpdated}
intl={props.intl}
indexPatterns={props.indexPatterns}
/>
)}
</EuiFlexItem>

<EuiFlexItem className="globalFilterGroup__filterFlexItem">
Expand Down
188 changes: 188 additions & 0 deletions src/plugins/data/public/ui/filter_bar/filter_options.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { FilterOptions } from './filter_options';
import { SavedQueryAttributes } from '../../query';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { Query } from 'src/plugins/data/common';
import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';

// Mock useOpenSearchDashboards hook
jest.mock('../../../../opensearch_dashboards_react/public', () => ({
useOpenSearchDashboards: jest.fn(),
withOpenSearchDashboards: (Component: any) => (props: any) => <Component {...props} />,
}));

const mockProps = () => ({
savedQueryService: {
saveQuery: jest.fn(),
getAllSavedQueries: jest.fn(),
findSavedQueries: jest.fn().mockResolvedValue({ total: 0, queries: [] }),
getSavedQuery: jest.fn(),
deleteSavedQuery: jest.fn(),
getSavedQueryCount: jest.fn(),
},
onSave: jest.fn(),
onSaveAsNew: jest.fn(),
onLoad: jest.fn(),
onClearSavedQuery: jest.fn(),
onFiltersUpdated: jest.fn(),
showSaveQuery: true,
loadedSavedQuery: {
id: '1',
attributes: {
name: 'Test Query',
title: '',
description: '',
query: { query: '', language: 'kuery' } as Query,
} as SavedQueryAttributes,
},
filters: [
{
meta: {
alias: null,
disabled: false,
negate: false,
},
},
],
indexPatterns: [],
useSaveQueryMenu: false,
});

describe('Filter options menu', () => {
beforeEach(() => {
// Mocking `uiSettings.get` to return true for `useNewHeader`
(useOpenSearchDashboards as jest.Mock).mockReturnValue({
services: {
uiSettings: {
get: jest.fn((key) => {
if (key === 'home:useNewHomePage') {
return true;
}
return false;
}),
},
},
});
});

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

it('render menu panel', () => {
const wrapper = mountWithIntl(<FilterOptions {...mockProps()} />);
const button = wrapper.find('[data-test-subj="showFilterActions"]').at(0);

button.simulate('click');
expect(wrapper.find('[data-test-subj="filter-options-menu-panel"]').exists()).toBeTruthy();
});

it("render filter options with 'Add filter' button", () => {
const wrapper = mountWithIntl(<FilterOptions {...mockProps()} />);
const button = wrapper.find('[data-test-subj="showFilterActions"]').at(0);
button.simulate('click');
wrapper.update();
const addFilterButton = wrapper.find('[data-test-subj="addFilters"]').at(0);
addFilterButton.simulate('click');
expect(wrapper.find('[data-test-subj="add-filter-panel"]').exists()).toBeTruthy();
});

it("render filter options with 'Save Query' button", () => {
const wrapper = mountWithIntl(<FilterOptions {...mockProps()} />);
const button = wrapper.find('[data-test-subj="showFilterActions"]').at(0);
button.simulate('click');
wrapper.update();
const saveQueryButton = wrapper
.find('[data-test-subj="saved-query-management-save-button"]')
.at(0);
expect(saveQueryButton.exists()).toBeTruthy();
saveQueryButton.simulate('click');
expect(wrapper.find('[data-test-subj="save-query-panel"]').exists()).toBeTruthy();
});

it('should call onFiltersUpdated when enable all filters button is clicked', () => {
const props = mockProps();
const wrapper = mountWithIntl(<FilterOptions {...props} />);
const button = wrapper.find('[data-test-subj="showFilterActions"]').at(0);
button.simulate('click');
wrapper.update();
const enableAllFiltersButton = wrapper.find('[data-test-subj="enableAllFilters"]').at(0);
enableAllFiltersButton.simulate('click');
expect(props.onFiltersUpdated).toHaveBeenCalled();
});

it('should call onFiltersUpdated when disable all filters button is clicked', () => {
const props = mockProps();
const wrapper = mountWithIntl(<FilterOptions {...props} />);
const button = wrapper.find('[data-test-subj="showFilterActions"]').at(0);
button.simulate('click');
wrapper.update();
const disableAllFiltersButton = wrapper.find('[data-test-subj="disableAllFilters"]').at(0);
disableAllFiltersButton.simulate('click');
expect(props.onFiltersUpdated).toHaveBeenCalled();
});

it('should call onFiltersUpdated when pin all filters button is clicked', () => {
const props = mockProps();
const wrapper = mountWithIntl(<FilterOptions {...props} />);
const button = wrapper.find('[data-test-subj="showFilterActions"]').at(0);
button.simulate('click');
wrapper.update();
const pinAllFiltersButton = wrapper.find('[data-test-subj="pinAllFilters"]').at(0);
pinAllFiltersButton.simulate('click');
expect(props.onFiltersUpdated).toHaveBeenCalled();
});

it('should call onFiltersUpdated when unpin all filters button is clicked', () => {
const props = mockProps();
const wrapper = mountWithIntl(<FilterOptions {...props} />);
const button = wrapper.find('[data-test-subj="showFilterActions"]').at(0);
button.simulate('click');
wrapper.update();
const unpinAllFiltersButton = wrapper.find('[data-test-subj="unpinAllFilters"]').at(0);
unpinAllFiltersButton.simulate('click');
expect(props.onFiltersUpdated).toHaveBeenCalled();
});

it('should call onFiltersUpdated when Invert all filters button is clicked', () => {
const props = mockProps();
const wrapper = mountWithIntl(<FilterOptions {...props} />);
const button = wrapper.find('[data-test-subj="showFilterActions"]').at(0);
button.simulate('click');
wrapper.update();
const invertAllFiltersButton = wrapper
.find('[data-test-subj="invertInclusionAllFilters"]')
.at(0);
invertAllFiltersButton.simulate('click');
expect(props.onFiltersUpdated).toHaveBeenCalled();
});

it('should call onFiltersUpdated when Invert enabled/disabled filters button is clicked', () => {
const props = mockProps();
const wrapper = mountWithIntl(<FilterOptions {...props} />);
const button = wrapper.find('[data-test-subj="showFilterActions"]').at(0);
button.simulate('click');
wrapper.update();
const invertEnabledDisabledFiltersButton = wrapper
.find('[data-test-subj="invertEnableDisableAllFilters"]')
.at(0);
invertEnabledDisabledFiltersButton.simulate('click');
expect(props.onFiltersUpdated).toHaveBeenCalled();
});

it('should call onFiltersUpdated when remove all filters button is clicked', () => {
const props = mockProps();
const wrapper = mountWithIntl(<FilterOptions {...props} />);
const button = wrapper.find('[data-test-subj="showFilterActions"]').at(0);
button.simulate('click');
wrapper.update();
const removeAllFiltersButton = wrapper.find('[data-test-subj="removeAllFilters"]').at(0);
removeAllFiltersButton.simulate('click');
expect(props.onFiltersUpdated).toHaveBeenCalled();
});
});
Loading
Loading