From f5dbae14da782f2ca77fdc617c0af4c026984e6f Mon Sep 17 00:00:00 2001 From: Sachin Chaurasiya Date: Mon, 13 Jun 2022 22:43:22 +0530 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20[Feature]=20:=20Advance=20Search=20?= =?UTF-8?q?(#5427)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vivek Ratnavel Subramanian --- .../ui/cypress/constants/constants.js | 6 +- .../integration/Pages/EntityDetails.spec.js | 2 +- .../resources/ui/src/axiosAPIs/miscAPI.ts | 19 +- .../ActivityThreadPanel.tsx | 4 + .../AdvancedSearch/AdvancedField.test.tsx | 164 ++++++++++++++++ .../AdvancedSearch/AdvancedField.tsx | 183 ++++++++++++++++++ .../AdvancedSearch/AdvancedFields.test.tsx | 73 +++++++ .../AdvancedSearch/AdvancedFields.tsx | 55 ++++++ .../AdvancedSearchDropDown.test.tsx | 114 +++++++++++ .../AdvancedSearch/AdvancedSearchDropDown.tsx | 55 ++++++ .../components/Explore/Explore.component.tsx | 158 ++++++++++----- .../Explore/SortingDropDown.test.tsx | 73 +++++++ .../components/Explore/SortingDropDown.tsx | 55 ++++++ .../components/Explore/explore.interface.ts | 5 + .../ui/src/components/app-bar/Appbar.tsx | 9 +- .../src/constants/advanceSearch.constants.ts | 66 +++++++ .../resources/ui/src/constants/constants.ts | 6 +- .../ui/src/constants/explore.constants.ts | 20 +- .../ui/src/enums/AdvancedSearch.enum.ts | 21 ++ .../pages/explore/ExplorePage.component.tsx | 5 +- .../main/resources/ui/src/styles/x-master.css | 19 ++ .../ui/src/utils/AdvancedSearchUtils.ts | 78 ++++++++ .../resources/ui/src/utils/SearchUtils.ts | 2 +- 23 files changed, 1118 insertions(+), 74 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedField.test.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedField.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedFields.test.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedFields.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearchDropDown.test.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearchDropDown.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Explore/SortingDropDown.test.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Explore/SortingDropDown.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/constants/advanceSearch.constants.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/enums/AdvancedSearch.enum.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.ts diff --git a/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js b/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js index 2b377dc109e..61eec8868cf 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js @@ -42,7 +42,7 @@ export const SEARCH_ENTITY_TOPIC = { export const SEARCH_ENTITY_DASHBOARD = { dashboard_1: { - term: 'Sales Dashboard', + term: 'Slack Dashboard', entity: MYDATA_SUMMARY_OPTIONS.dashboards, }, dashboard_2: { @@ -52,9 +52,9 @@ export const SEARCH_ENTITY_DASHBOARD = { }; export const SEARCH_ENTITY_PIPELINE = { - pipeline_1: { term: 'Hive ETL', entity: MYDATA_SUMMARY_OPTIONS.pipelines }, + pipeline_1: { term: 'Snowflake ETL', entity: MYDATA_SUMMARY_OPTIONS.pipelines }, pipeline_2: { - term: 'Snowflake ETL', + term: 'Hive ETL', entity: MYDATA_SUMMARY_OPTIONS.pipelines, }, pipeline_3: { diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/EntityDetails.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/EntityDetails.spec.js index 8d7a57c61c4..3e6a8fa177a 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/EntityDetails.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/EntityDetails.spec.js @@ -32,7 +32,7 @@ describe('Entity Details Page', () => { // click on the 1st result and go to manage tab in entity details page cy.wait(500); cy.get('[data-testid="table-link"]').first().should('be.visible').click(); - cy.get('[data-testid="Manage"]').scrollIntoView().click(); + cy.get('[data-testid="Manage"]').click(); // check for delete section and delete button is available or not cy.get('[data-testid="danger-zone"]').scrollIntoView().should('be.visible'); diff --git a/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/miscAPI.ts b/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/miscAPI.ts index 345107b0cd7..e97941b410d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/miscAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/miscAPI.ts @@ -132,9 +132,12 @@ export const getSuggestedTeams = (term: string): Promise => { export const getUserSuggestions: Function = ( term: string ): Promise => { - return APIClient.get( - `/search/suggest?q=${term}&index=${SearchIndex.USER},${SearchIndex.TEAM}` - ); + const params = { + q: term, + index: `${SearchIndex.USER},${SearchIndex.TEAM}`, + }; + + return APIClient.get(`/search/suggest`, { params }); }; export const getSearchedUsers = ( @@ -193,3 +196,13 @@ export const deleteEntity: Function = ( return APIClient.delete(path); }; + +export const getAdvancedFieldOptions = ( + q: string, + index: string, + field: string | undefined +): Promise => { + const params = { index, field, q }; + + return APIClient.get(`/search/suggest`, { params }); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel.tsx index 646db759fb7..b5c2310d1ca 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel.tsx @@ -184,6 +184,10 @@ const ActivityThreadPanel: FC = ({ fetchMoreThread(isInView as boolean, paging, isThreadLoading); }, [paging, isThreadLoading, isInView]); + useEffect(() => { + document.body.style.overflow = 'hidden'; + }, []); + return ReactDOM.createPortal(
({ + getAdvancedFieldOptions: jest + .fn() + .mockImplementation(() => Promise.resolve()), + getUserSuggestions: jest + .fn() + .mockImplementation(() => Promise.resolve({ data: { suggest: mockData } })), +})); + +const index = 'table_search_index'; +const field = { key: 'owner.name', value: undefined } as AdvanceField; +const onFieldRemove = jest.fn(); +const onFieldValueSelect = jest.fn(); + +const mockProps = { + index, + field, + onFieldRemove, + onFieldValueSelect, +}; + +describe('Test AdvancedField Component', () => { + it('Should render advancedfield component', async () => { + const { findByTestId } = render(); + + const label = await findByTestId('field-label'); + + expect(label).toBeInTheDocument(); + + expect(label).toHaveTextContent('Owner:'); + + const searchSelect = await findByTestId('field-select'); + + expect(searchSelect).toBeInTheDocument(); + + const removeButton = await findByTestId('field-remove-button'); + + expect(removeButton).toBeInTheDocument(); + }); + + it('Should call remove method on click of remove button', async () => { + const { findByTestId } = render(); + + const label = await findByTestId('field-label'); + + expect(label).toBeInTheDocument(); + + expect(label).toHaveTextContent('Owner:'); + + const searchSelect = await findByTestId('field-select'); + + expect(searchSelect).toBeInTheDocument(); + + const removeButton = await findByTestId('field-remove-button'); + + expect(removeButton).toBeInTheDocument(); + + fireEvent.click(removeButton); + + expect(onFieldRemove).toHaveBeenCalledWith(field.key); + }); + + it('Should call select method on click of option', async () => { + const { findByTestId, findByRole, findAllByTestId } = render( + + ); + + const label = await findByTestId('field-label'); + + expect(label).toBeInTheDocument(); + + expect(label).toHaveTextContent('Owner:'); + + const searchSelect = await findByTestId('field-select'); + + expect(searchSelect).toBeInTheDocument(); + + const removeButton = await findByTestId('field-remove-button'); + + expect(removeButton).toBeInTheDocument(); + + const searchInput = await findByRole('combobox'); + + expect(searchInput).toBeInTheDocument(); + + fireEvent.change(searchInput, { target: { value: 'cloud' } }); + + const fieldOptions = await findAllByTestId('field-option'); + + expect(fieldOptions).toHaveLength( + mockData['metadata-suggest'][0].options.length + ); + + fireEvent.click(fieldOptions[0]); + + expect(onFieldValueSelect).toHaveBeenCalledWith({ + key: 'owner.name', + value: 'Cloud_Infra', + }); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedField.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedField.tsx new file mode 100644 index 00000000000..db34a098cf4 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedField.tsx @@ -0,0 +1,183 @@ +/* + * Copyright 2021 Collate + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Select } from 'antd'; +import { AxiosError, AxiosResponse } from 'axios'; +import { startCase } from 'lodash'; +import React, { FC, useState } from 'react'; +import { + getAdvancedFieldOptions, + getUserSuggestions, +} from '../../axiosAPIs/miscAPI'; +import { MISC_FIELDS } from '../../constants/advanceSearch.constants'; +import { + getAdvancedField, + getItemLabel, +} from '../../utils/AdvancedSearchUtils'; +import { showErrorToast } from '../../utils/ToastUtils'; +import { AdvanceField } from '../Explore/explore.interface'; + +interface Props { + index: string; + field: AdvanceField; + onFieldRemove: (value: string) => void; + onFieldValueSelect: (field: AdvanceField) => void; +} + +interface Option { + label: string; + value: string; +} + +interface InputProps { + options: Option[]; + value: string | undefined; + handleChange: (value: string) => void; + handleSearch: (value: string) => void; + handleSelect: (value: string) => void; + handleClear: () => void; +} + +const SearchInput = ({ + options, + value, + handleChange, + handleSearch, + handleSelect, + handleClear, +}: InputProps) => { + const { Option } = Select; + + const optionsElement = options.map((d) => ( + + )); + + return ( + + ); +}; + +const AdvancedField: FC = ({ + field, + onFieldRemove, + index, + onFieldValueSelect, +}) => { + const advancedField = getAdvancedField(field.key); + + const [options, setOptions] = useState([]); + const [value, setValue] = useState(field.value); + + const fetchOptions = (query: string) => { + if (!MISC_FIELDS.includes(field.key)) { + getAdvancedFieldOptions(query, index, advancedField) + .then((res: AxiosResponse) => { + const suggestOptions = + res.data.suggest['metadata-suggest'][0].options ?? []; + const uniqueOptions = [ + // eslint-disable-next-line + ...new Set(suggestOptions.map((op: any) => op.text)), + ]; + setOptions( + uniqueOptions.map((op: unknown) => ({ + label: op as string, + value: op as string, + })) + ); + }) + .catch((err: AxiosError) => showErrorToast(err)); + } else { + getUserSuggestions(query) + .then((res: AxiosResponse) => { + const suggestOptions = + res.data.suggest['metadata-suggest'][0].options ?? []; + const uniqueOptions = [ + // eslint-disable-next-line + ...new Set(suggestOptions.map((op: any) => op._source.name)), + ]; + setOptions( + uniqueOptions.map((op: unknown) => ({ + label: op as string, + value: op as string, + })) + ); + }) + .catch((err: AxiosError) => showErrorToast(err)); + } + }; + + const handleSearch = (newValue: string) => { + if (newValue) { + fetchOptions(newValue); + } else { + setOptions([]); + } + }; + + const handleChange = (newValue: string) => { + setValue(newValue); + }; + + const handleOnSelect = (newValue: string) => { + onFieldValueSelect({ ...field, value: newValue }); + }; + + const handleOnClear = () => { + onFieldValueSelect({ ...field, value: undefined }); + }; + + return ( +
+ + {startCase(getItemLabel(field.key))}: + + + onFieldRemove(field.key)}> + + +
+ ); +}; + +export default AdvancedField; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedFields.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedFields.test.tsx new file mode 100644 index 00000000000..ebe399c82ba --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedFields.test.tsx @@ -0,0 +1,73 @@ +/* + * Copyright 2021 Collate + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { fireEvent, render } from '@testing-library/react'; +import React from 'react'; +import { AdvanceField } from '../Explore/explore.interface'; +import AdvancedFields from './AdvancedFields'; + +jest.mock('./AdvancedField', () => + jest + .fn() + .mockReturnValue(
AdvancedField
) +); + +const index = 'table_search_index'; +const fields = [ + { key: 'owner.name', value: undefined }, + { key: 'column_names', value: undefined }, +] as AdvanceField[]; + +const onFieldRemove = jest.fn(); +const onClear = jest.fn(); +const onFieldValueSelect = jest.fn(); + +const mockProps = { + index, + fields, + onFieldRemove, + onClear, + onFieldValueSelect, +}; + +describe('Test AdvancedFields component', () => { + it('Should render AdvancedFields component', async () => { + const { findByTestId, findAllByTestId } = render( + + ); + + const fields = await findAllByTestId('advanced-field'); + const clearButton = await findByTestId('clear-all-button'); + + expect(fields).toHaveLength(fields.length); + + expect(clearButton).toBeInTheDocument(); + }); + + it('Should call onClear method on click of Clear All button', async () => { + const { findByTestId, findAllByTestId } = render( + + ); + + const fields = await findAllByTestId('advanced-field'); + const clearButton = await findByTestId('clear-all-button'); + + expect(fields).toHaveLength(fields.length); + + expect(clearButton).toBeInTheDocument(); + + fireEvent.click(clearButton); + + expect(onClear).toBeCalled(); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedFields.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedFields.tsx new file mode 100644 index 00000000000..e68692e5773 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedFields.tsx @@ -0,0 +1,55 @@ +/* + * Copyright 2021 Collate + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { uniqueId } from 'lodash'; +import React, { FC } from 'react'; +import { AdvanceField } from '../Explore/explore.interface'; +import AdvancedField from './AdvancedField'; + +interface Props { + index: string; + fields: Array; + onFieldRemove: (value: string) => void; + onClear: () => void; + onFieldValueSelect: (field: AdvanceField) => void; +} + +const AdvancedFields: FC = ({ + fields, + onFieldRemove, + onClear, + index, + onFieldValueSelect, +}) => { + return ( +
+ {fields.map((field) => ( + + ))} + + Clear All + +
+ ); +}; + +export default AdvancedFields; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearchDropDown.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearchDropDown.test.tsx new file mode 100644 index 00000000000..2e24978c0ff --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearchDropDown.test.tsx @@ -0,0 +1,114 @@ +/* + * Copyright 2021 Collate + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { fireEvent, render } from '@testing-library/react'; +import React from 'react'; +import { + COMMON_DROPDOWN_ITEMS, + TABLE_DROPDOWN_ITEMS, +} from '../../constants/advanceSearch.constants'; +import { AdvanceField } from '../Explore/explore.interface'; +import AdvancedSearchDropDown from './AdvancedSearchDropDown'; + +const mockItems = [...COMMON_DROPDOWN_ITEMS, ...TABLE_DROPDOWN_ITEMS]; + +jest.mock('../../utils/AdvancedSearchUtils', () => ({ + getDropDownItems: jest + .fn() + .mockReturnValue([...COMMON_DROPDOWN_ITEMS, ...TABLE_DROPDOWN_ITEMS]), +})); + +const onSelect = jest.fn(); +const selectedItems = [] as AdvanceField[]; +const index = 'table_search_index'; + +const mockPorps = { + selectedItems, + index, + onSelect, +}; + +describe('Test AdvancedSearch DropDown Component', () => { + it('Should render dropdown component', async () => { + const { findByTestId, findAllByTestId } = render( + + ); + + const dropdownLabel = await findByTestId('dropdown-label'); + + expect(dropdownLabel).toBeInTheDocument(); + + fireEvent.click(dropdownLabel); + + const dropdownMenu = await findByTestId('dropdown-menu'); + + expect(dropdownMenu).toBeInTheDocument(); + + const menuItems = await findAllByTestId('dropdown-menu-item'); + + expect(menuItems).toHaveLength(mockItems.length); + }); + + it('Should call onSelect method on onClick option', async () => { + const { findByTestId, findAllByTestId } = render( + + ); + + const dropdownLabel = await findByTestId('dropdown-label'); + + expect(dropdownLabel).toBeInTheDocument(); + + fireEvent.click(dropdownLabel); + + const dropdownMenu = await findByTestId('dropdown-menu'); + + expect(dropdownMenu).toBeInTheDocument(); + + const menuItems = await findAllByTestId('dropdown-menu-item'); + + expect(menuItems).toHaveLength(mockItems.length); + + fireEvent.click(menuItems[0]); + + expect(onSelect).toHaveBeenCalledWith(mockItems[0].key); + }); + + it('Selected option should be disabled', async () => { + const { findByTestId, findAllByTestId } = render( + + ); + + const dropdownLabel = await findByTestId('dropdown-label'); + + expect(dropdownLabel).toBeInTheDocument(); + + fireEvent.click(dropdownLabel); + + const dropdownMenu = await findByTestId('dropdown-menu'); + + expect(dropdownMenu).toBeInTheDocument(); + + const menuItems = await findAllByTestId('dropdown-menu-item'); + + expect(menuItems).toHaveLength(mockItems.length); + + expect(menuItems[0]).toHaveAttribute('aria-disabled', 'true'); + + fireEvent.click(menuItems[0]); + + expect(onSelect).not.toHaveBeenCalledWith(mockItems[0].key); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearchDropDown.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearchDropDown.tsx new file mode 100644 index 00000000000..c4efc95fe25 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearchDropDown.tsx @@ -0,0 +1,55 @@ +/* + * Copyright 2021 Collate + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Dropdown, Menu } from 'antd'; +import React, { FC } from 'react'; +import { getDropDownItems } from '../../utils/AdvancedSearchUtils'; +import { normalLink } from '../../utils/styleconstant'; +import { dropdownIcon as DropdownIcon } from '../../utils/svgconstant'; +import { AdvanceField } from '../Explore/explore.interface'; + +interface Props { + index: string; + selectedItems: Array; + onSelect: (filter: string) => void; +} + +const AdvancedSearchDropDown: FC = ({ + index, + onSelect, + selectedItems, +}) => { + const items = getDropDownItems(index).map((item) => ({ + ...item, + onClick: () => onSelect(item.key), + disabled: selectedItems.some((i) => item.key === i.key), + 'data-testid': 'dropdown-menu-item', + })); + + const menu = ; + + return ( + +
+ Advanced Search + +
+
+ ); +}; + +export default AdvancedSearchDropDown; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.component.tsx index a09bde3e891..3925189c069 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.component.tsx @@ -36,7 +36,6 @@ import { useHistory, useLocation } from 'react-router-dom'; import { Button } from '../../components/buttons/Button/Button'; import ErrorPlaceHolderES from '../../components/common/error-with-placeholder/ErrorPlaceHolderES'; import FacetFilter from '../../components/common/facetfilter/FacetFilter'; -import DropDownList from '../../components/dropdown/DropDownList'; import SearchedData from '../../components/searched-data/SearchedData'; import { getExplorePathWithSearch, @@ -66,9 +65,11 @@ import { import { formatDataResponse } from '../../utils/APIUtils'; import { getCountBadge } from '../../utils/CommonUtils'; import { getFilterCount, getFilterString } from '../../utils/FilterUtils'; -import { dropdownIcon as DropDownIcon } from '../../utils/svgconstant'; +import AdvancedFields from '../AdvancedSearch/AdvancedFields'; +import AdvancedSearchDropDown from '../AdvancedSearch/AdvancedSearchDropDown'; import PageLayout from '../containers/PageLayout'; -import { ExploreProps } from './explore.interface'; +import { AdvanceField, ExploreProps } from './explore.interface'; +import SortingDropDown from './SortingDropDown'; const Explore: React.FC = ({ tabCounts, @@ -105,18 +106,16 @@ const Explore: React.FC = ({ ...filterObject, ...searchFilter, }); + const [currentPage, setCurrentPage] = useState(1); const [totalNumberOfValue, setTotalNumberOfValues] = useState(0); const [aggregations, setAggregations] = useState>([]); const [searchTag, setSearchTag] = useState(location.search); - - const [fieldListVisible, setFieldListVisible] = useState(false); const [sortField, setSortField] = useState(sortValue); const [sortOrder, setSortOrder] = useState(INITIAL_SORT_ORDER); const [searchIndex, setSearchIndex] = useState(getCurrentIndex(tab)); const [currentTab, setCurrentTab] = useState(getCurrentTab(tab)); - const [fieldList, setFieldList] = - useState>(tableSortingFields); + const [isEntityLoading, setIsEntityLoading] = useState(true); const [isFilterSet, setIsFilterSet] = useState( !isEmpty(initialFilter) @@ -125,6 +124,43 @@ const Explore: React.FC = ({ const isMounting = useRef(true); const forceSetAgg = useRef(false); const previsouIndex = usePrevious(searchIndex); + const [fieldList, setFieldList] = + useState>(tableSortingFields); + + const [selectedAdvancedFields, setSelectedAdvancedField] = useState< + Array + >([]); + + const onAdvancedFieldSelect = (value: string) => { + const flag = selectedAdvancedFields.some((field) => field.key === value); + if (!flag) { + setSelectedAdvancedField((pre) => [ + ...pre, + { key: value, value: undefined }, + ]); + } + }; + const onAdvancedFieldRemove = (value: string) => { + setSelectedAdvancedField((pre) => + pre.filter((field) => field.key !== value) + ); + }; + + const onAdvancedFieldClear = () => { + setSelectedAdvancedField([]); + }; + + const onAdvancedFieldValueSelect = (field: AdvanceField) => { + setSelectedAdvancedField((pre) => { + return pre.map((preField) => { + if (preField.key === field.key) { + return field; + } else { + return preField; + } + }); + }); + }; const handleSelectedFilter = ( checked: boolean, @@ -160,11 +196,16 @@ const Explore: React.FC = ({ handleFilterChange(filterData); }; + const handleFieldDropDown = (value: string) => { + setSortField(value); + }; + const handleShowDeleted = (checked: boolean) => { onShowDeleted(checked); }; const onClearFilterHandler = (type: string[], isForceClear = false) => { + setSelectedAdvancedField([]); const updatedFilter = type.reduce((filterObj, type) => { return { ...filterObj, [type]: [] }; }, {}); @@ -335,44 +376,26 @@ const Explore: React.FC = ({ return facetFilters; }; - const handleFieldDropDown = ( - _e: React.MouseEvent, - value?: string - ) => { - setSortField(value || sortField); - setFieldListVisible(false); - }; const handleOrder = (value: string) => { setSortOrder(value); }; const getSortingElements = () => { return ( -
-
- Sort by: - - - {fieldListVisible && ( - - )} - -
-
+
+ + + + +
{sortOrder === 'asc' ? (