Skip to content

Commit

Permalink
Refactor search bar & filters to conditionally render new look with a…
Browse files Browse the repository at this point in the history
…pplication header (#7687) (#7719)

* Refactor search bar & filters to conditionally render with new application header



* add more test coverage



* address comments



* Changeset file for PR #7687 created/updated

---------



(cherry picked from commit 97ddd8a)

Signed-off-by: Zhongnan Su <[email protected]>
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Aug 15, 2024
1 parent 32c8ccf commit b3078c4
Show file tree
Hide file tree
Showing 10 changed files with 731 additions and 400 deletions.
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 {
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

0 comments on commit b3078c4

Please sign in to comment.