From ca7853c5a4d7bddb6c0bdbdedd0bc9092f491bca Mon Sep 17 00:00:00 2001 From: Anna Everhart <149417426+annadoesdesign@users.noreply.github.com> Date: Mon, 5 May 2025 11:44:38 -0700 Subject: [PATCH] Update search results page (#13303) --- .../components/Pills/utils.ts | 2 +- .../src/app/searchV2/SearchPage.tsx | 13 +- .../src/app/searchV2/SearchResults.tsx | 156 +++--------------- .../searchV2/filters/SearchFilterOptions.tsx | 24 --- .../app/searchV2/filters/SearchFilters.tsx | 135 +++++++++++++-- .../searchV2/filters/SearchFiltersSection.tsx | 18 +- .../searchV2/recommendation/FilterPill.tsx | 53 ++---- .../recommendation/RecommendedFilters.tsx | 5 - .../app/sharedV2/search/DownloadButton.tsx | 18 +- .../src/app/sharedV2/search/EditButton.tsx | 15 +- .../lineageV2/v2_download_lineage_results.js | 2 +- 11 files changed, 207 insertions(+), 234 deletions(-) diff --git a/datahub-web-react/src/alchemy-components/components/Pills/utils.ts b/datahub-web-react/src/alchemy-components/components/Pills/utils.ts index abab592e1f..f1284559f0 100644 --- a/datahub-web-react/src/alchemy-components/components/Pills/utils.ts +++ b/datahub-web-react/src/alchemy-components/components/Pills/utils.ts @@ -45,7 +45,7 @@ const getPillVariantStyles = (variant: PillVariantOptions, colorStyles: ColorSty }, outline: { backgroundColor: 'transparent', - border: `1px solid ${colorStyles.primaryColor}`, + border: `1px solid ${colorStyles.bgColor}`, color: colorStyles.primaryColor, '&:hover': { backgroundColor: colorStyles.hoverColor, diff --git a/datahub-web-react/src/app/searchV2/SearchPage.tsx b/datahub-web-react/src/app/searchV2/SearchPage.tsx index 2ebf74de15..2cce0385b6 100644 --- a/datahub-web-react/src/app/searchV2/SearchPage.tsx +++ b/datahub-web-react/src/app/searchV2/SearchPage.tsx @@ -240,17 +240,18 @@ export const SearchPage = () => { onChangeFilters={onChangeFilters} onClearFilters={onClearFilters} onChangeUnionType={onChangeUnionType} - /> - + ` display: flex; @@ -69,15 +60,12 @@ const ResultContainer = styled.div<{ v2Styles: boolean }>` `; const PaginationControlContainer = styled.div` - padding-top: 16px; - padding-bottom: 16px; - text-align: center; + padding: 16px; + text-align: start; `; const PaginationInfoContainer = styled.div<{ v2Styles: boolean }>` - padding: 12px 24px 14px 24px; - min-height: 47px; - border-color: ${(props) => props.theme.styles['border-color-base']}; + padding: 4px 8px 4px 12px; display: flex; justify-content: space-between; align-items: center; @@ -97,7 +85,8 @@ const SearchResultsScrollContainer = styled.div<{ $isShowNavBarRedesign?: boolea const LeftControlsContainer = styled.div` display: flex; - gap: 12px; + color: ${colors.gray[1700]}; + gap: 4px; `; const StyledTabToolbar = styled.div` @@ -111,11 +100,6 @@ const StyledTabToolbar = styled.div` border: 1.5px solid ${ANTD_GRAY[4]}; `; -const SearchMenuContainer = styled.div` - display: flex; - align-items: center; -`; - const SearchResultListContainer = styled.div<{ v2Styles: boolean; $isShowNavBarRedesign?: boolean }>` display: flex; flex-direction: column; @@ -130,41 +114,9 @@ const SearchResultListContainer = styled.div<{ v2Styles: boolean; $isShowNavBarR margin: ${(props) => (props.$isShowNavBarRedesign ? '5px 4px 5px 0px' : '4px 12px 4px 0px')}; `; -const CustomSwitch = styled.div` - background: #F6F6F6; - border: 1px solid #EBECF0; - border-radius: 30px; - display: flex; - gap: 2px; - align-items: center; - padding: 2px; - width: fit-content; - justify-content: space-between; - margin-left: 8px; -} -`; - -const IconContainer = styled.div<{ isActive?: boolean }>` - cursor: pointer; - align-items: center; - display: flex; - padding: 4px; - transition: left 0.5s ease; - - ${(props) => - props.isActive && - ` - background: ${props.theme.styles['primary-color']}; - border-radius: 100%; - color: white; - `} -`; - interface Props { loading: boolean; - unionType?: UnionType; query: string; - viewUrn?: string; page: number; searchResponse?: { start: number; @@ -175,12 +127,10 @@ interface Props { matchedFields: MatchedField[]; }[]; } | null; - availableFilters: Array | null; selectedFilters: Array; error: any; onChangeFilters: (filters: Array) => void; onChangePage: (page: number) => void; - downloadSearchResults: (input: DownloadSearchResultsInput) => Promise; numResultsPerPage: number; setNumResultsPerPage: (numResults: number) => void; isSelectMode: boolean; @@ -198,26 +148,22 @@ interface Props { export const SearchResults = ({ loading, - unionType = UnionType.AND, query, - viewUrn, page, searchResponse, - availableFilters, selectedFilters, error, onChangeFilters, onChangePage, - downloadSearchResults, numResultsPerPage, setNumResultsPerPage, isSelectMode, selectedEntities, + suggestions, + setSelectedEntities, areAllEntitiesSelected, setAreAllEntitiesSelected, - suggestions, setIsSelectMode, - setSelectedEntities, onChangeSelectAll, refetch, previewType, @@ -235,10 +181,8 @@ export const SearchResults = ({ showSeparateSiblings, searchResponse?.searchResults, ); - const { selectedSortOption, setSelectedSortOption } = useSearchContext(); // For vertical sidebar const [highlightedIndex, setHighlightedIndex] = useState(0); - const { isFullViewCard, setIsFullViewCard } = useSearchContext(); const searchResultUrns = combinedSiblingSearchResults.map((result) => result.entity.urn) || []; const selectedEntityUrns = selectedEntities.map((entity) => entity.urn); @@ -279,70 +223,8 @@ export const SearchResults = ({ v2Styles={showSearchFiltersV2} $isShowNavBarRedesign={isShowNavBarRedesign} > - - - - Showing{' '} - - {lastResultIndex > 0 ? (page - 1) * pageSize + 1 : 0} -{' '} - {lastResultIndex} - {' '} - of{' '} - - {totalResults >= 10000 - ? `${formatNumberWithoutAbbreviation(10000)}+` - : formatNumberWithoutAbbreviation(totalResults)} - {' '} - results - - - - - - - setIsFullViewCard(true)} - > - - - - - setIsFullViewCard(false)} - > - - - - - - - {totalResults > 0 && } - + {isSelectMode && ( )} + + + Showing{' '} + + {lastResultIndex > 0 ? (page - 1) * pageSize + 1 : 0} -{' '} + {lastResultIndex} + {' '} + of{' '} + + {totalResults >= 10000 + ? `${formatNumberWithoutAbbreviation(10000)}+` + : formatNumberWithoutAbbreviation(totalResults)} + {' '} + results + + - - - Filters - - {loading && !visibleFilters?.length && } {visibleFilters?.map((filter) => { return filterRendererRegistry.hasRenderer(filter.field) ? ( diff --git a/datahub-web-react/src/app/searchV2/filters/SearchFilters.tsx b/datahub-web-react/src/app/searchV2/filters/SearchFilters.tsx index ce4e0e51f6..f5dad7b885 100644 --- a/datahub-web-react/src/app/searchV2/filters/SearchFilters.tsx +++ b/datahub-web-react/src/app/searchV2/filters/SearchFilters.tsx @@ -1,9 +1,14 @@ +import { Icon, Tooltip, colors } from '@components'; import React from 'react'; import styled from 'styled-components'; import { SEARCH_RESULTS_FILTERS_ID } from '@app/onboarding/config/SearchOnboardingConfig'; +import { useSearchContext } from '@app/search/context/SearchContext'; import SearchFilterOptions from '@app/searchV2/filters/SearchFilterOptions'; import SelectedSearchFilters from '@app/searchV2/filters/SelectedSearchFilters'; +import { RecommendedFilters } from '@app/searchV2/recommendation/RecommendedFilters'; +import { useGetRecommendedFilters } from '@app/searchV2/recommendation/useGetRecommendedFilters'; +import SearchSortSelect from '@app/searchV2/sorting/SearchSortSelect'; import { BROWSE_PATH_V2_FILTER_NAME, COMPLETED_FORMS_COMPLETED_PROMPT_IDS_FILTER_NAME, @@ -20,22 +25,62 @@ import { UnionType, VERIFIED_FORMS_FILTER_NAME, } from '@app/searchV2/utils/constants'; +import { generateOrFilters } from '@app/searchV2/utils/generateOrFilters'; +import { DownloadSearchResults, DownloadSearchResultsInput } from '@app/searchV2/utils/types'; +import SearchMenuItems from '@app/sharedV2/search/SearchMenuItems'; import { useShowNavBarRedesign } from '@src/app/useShowNavBarRedesign'; import { FacetFilterInput, FacetMetadata } from '@types'; const Container = styled.div<{ $isShowNavBarRedesign?: boolean }>` - background-color: #ffffff; + background-color: ${colors.white}; border-radius: ${(props) => props.$isShowNavBarRedesign ? props.theme.styles['border-radius-navbar-redesign'] : '8px'}; - padding: 16px 24px 16px 32px; - border: 1px solid #e8e8e8; + padding: 16px 0px 8px 0px; + border: 1px solid ${colors.gray[100]}; box-shadow: ${(props) => props.$isShowNavBarRedesign ? props.theme.styles['box-shadow-navbar-redesign'] : '0px 4px 10px 0px #a8a8a840'}; `; -const FilterSpacer = styled.div` - margin: 16px 0px; +const FiltersContainerTop = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + padding-left: 16px; + padding-right: 16px; + padding-bottom: 8px; +`; + +const CustomSwitch = styled.div` + border: 1px solid ${colors.gray[100]}; + border-radius: 30px; + display: flex; + gap: 2px; + align-items: center; + padding: 2px; + width: fit-content; + justify-content: space-between; +`; + +const IconContainer = styled.div<{ isActive?: boolean }>` + cursor: pointer; + align-items: center; + display: flex; + padding: 4px; + transition: left 0.5s ease; + color: ${colors.gray[1800]}; + + ${(props) => + props.isActive && + ` + background: ${colors.gray[100]}; + border-radius: 100%; + color: ${colors.gray[1700]}; + `} +`; + +const SelectedFiltersContainer = styled.div` + padding: 4px 16px 8px 16px; `; // remove legacy filter options as well as new _index and browsePathV2 filter from dropdowns @@ -56,6 +101,18 @@ const FILTERS_TO_REMOVE = [ PROPOSED_SCHEMA_TAGS_FILTER_NAME, ]; +const ControlsContainer = styled.div` + display: flex; + align-items: center; + gap: 8px; + align-self: start; +`; + +const RecommendedFiltersContainer = styled.div` + border-top: 1px solid ${colors.gray[100]}; + padding: 16px 16px 8px 16px; +`; + interface Props { loading: boolean; availableFilters: FacetMetadata[]; @@ -65,6 +122,11 @@ interface Props { onChangeFilters: (newFilters: FacetFilterInput[]) => void; onChangeUnionType: (unionType: UnionType) => void; onClearFilters: () => void; + query: string; + viewUrn?: string; + totalResults?: number; + setShowSelectMode?: (showSelectMode: boolean) => any; + downloadSearchResults: (input: DownloadSearchResultsInput) => Promise; } export default function SearchFilters({ @@ -76,24 +138,58 @@ export default function SearchFilters({ onChangeFilters, onChangeUnionType, onClearFilters, + query, + viewUrn, + totalResults, + setShowSelectMode, + downloadSearchResults, }: Props) { const isShowNavBarRedesign = useShowNavBarRedesign(); + const { isFullViewCard, setIsFullViewCard, selectedSortOption, setSelectedSortOption } = useSearchContext(); // Filter out the available filters if `basicFilters` is true const filteredFilters = (availableFilters || []).filter((f) => !FILTERS_TO_REMOVE.includes(f.field)); const filters = basicFilters ? filteredFilters : availableFilters; + const recommendedFilters = useGetRecommendedFilters(filters, activeFilters); return ( - + + + + + + + setIsFullViewCard(true)}> + + + + + setIsFullViewCard(false)}> + + + + + + + {activeFilters.length > 0 && ( - <> - + - + + )} + {(totalResults || 0) > 0 && recommendedFilters.length > 0 && ( + + + )} ); diff --git a/datahub-web-react/src/app/searchV2/filters/SearchFiltersSection.tsx b/datahub-web-react/src/app/searchV2/filters/SearchFiltersSection.tsx index bc78b547d1..dba55202a4 100644 --- a/datahub-web-react/src/app/searchV2/filters/SearchFiltersSection.tsx +++ b/datahub-web-react/src/app/searchV2/filters/SearchFiltersSection.tsx @@ -4,6 +4,7 @@ import styled from 'styled-components'; import { SEARCH_RESULTS_FILTERS_V2_INTRO } from '@app/onboarding/config/SearchOnboardingConfig'; import SearchFilters from '@app/searchV2/filters/SearchFilters'; import { UnionType } from '@app/searchV2/utils/constants'; +import { DownloadSearchResults, DownloadSearchResultsInput } from '@app/searchV2/utils/types'; import { useShowNavBarRedesign } from '@src/app/useShowNavBarRedesign'; import { FacetFilterInput, FacetMetadata } from '@types'; @@ -26,6 +27,11 @@ interface Props { onChangeFilters: (newFilters: FacetFilterInput[]) => void; onClearFilters: () => void; onChangeUnionType: (unionType: UnionType) => void; + query: string; + viewUrn?: string; + totalResults: number; + setShowSelectMode?: (showSelectMode: boolean) => any; + downloadSearchResults: (input: DownloadSearchResultsInput) => Promise; } export default function SearchFiltersSection({ @@ -36,6 +42,11 @@ export default function SearchFiltersSection({ onChangeFilters, onClearFilters, onChangeUnionType, + query, + viewUrn, + totalResults, + setShowSelectMode, + downloadSearchResults, }: Props) { const [finalAvailableFilters, setFinalAvailableFilters] = useState(availableFilters); const isShowNavBarRedesign = useShowNavBarRedesign(); @@ -61,8 +72,13 @@ export default function SearchFiltersSection({ activeFilters={activeFilters} unionType={unionType} onChangeFilters={onChangeFilters} - onChangeUnionType={onChangeUnionType} onClearFilters={onClearFilters} + onChangeUnionType={onChangeUnionType} + query={query} + viewUrn={viewUrn} + totalResults={totalResults} + setShowSelectMode={setShowSelectMode} + downloadSearchResults={downloadSearchResults} /> ); diff --git a/datahub-web-react/src/app/searchV2/recommendation/FilterPill.tsx b/datahub-web-react/src/app/searchV2/recommendation/FilterPill.tsx index b71ffb4d71..33fa1a18a3 100644 --- a/datahub-web-react/src/app/searchV2/recommendation/FilterPill.tsx +++ b/datahub-web-react/src/app/searchV2/recommendation/FilterPill.tsx @@ -1,40 +1,7 @@ -import { Tooltip } from '@components'; +import { Pill, Tooltip } from '@components'; import React from 'react'; -import styled from 'styled-components'; import { RecommendedFilter } from '@app/searchV2/recommendation/types'; -import { getFilterColor } from '@app/searchV2/recommendation/utils'; - -const Pill = styled.div<{ color: string }>` - border-radius: 20px; - padding: 6px 10px; - background-color: white; - margin-right: 0px; - border: 2px solid white; - :hover { - opacity: 1; - cursor: pointer; - border-color: ${(props) => props.color}; - } - font-size: 14px; - display: flex; - align-items: center; - box-shadow: ${(props) => props.theme.styles['box-shadow']}; -`; - -const Icon = styled.div` - margin-right: 4px; - &&&& { - color: #ffffff; - } - display: flex; -`; - -const Text = styled.div` - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -`; type Props = { filter: RecommendedFilter; @@ -42,7 +9,11 @@ type Props = { }; export const FilterPill = ({ filter, onToggle }: Props) => { - const color = getFilterColor(filter.field, filter.value); + // Convert the color to a valid ColorValues enum value, defaulting to gray if not found + + // Convert ReactNode label to string + const labelString = typeof filter.label === 'string' ? filter.label : filter.label?.toString() || ''; + return ( { } > - - {filter.icon && {filter.icon}} - {filter.label} - + filter.icon} + color="gray" + variant="outline" + clickable + onPillClick={onToggle} + /> ); }; diff --git a/datahub-web-react/src/app/searchV2/recommendation/RecommendedFilters.tsx b/datahub-web-react/src/app/searchV2/recommendation/RecommendedFilters.tsx index 3f169e0378..60ab29893a 100644 --- a/datahub-web-react/src/app/searchV2/recommendation/RecommendedFilters.tsx +++ b/datahub-web-react/src/app/searchV2/recommendation/RecommendedFilters.tsx @@ -10,9 +10,6 @@ import { FacetFilterInput, FacetMetadata, FilterOperator } from '@types'; const FilterPills = styled.div` display: flex; flex-direction: row; - margin-bottom: 8px; - margin-left: 20px; - padding-right: 20px; gap: 8px; flex-shrink: 0; overflow-x: auto; @@ -21,8 +18,6 @@ const FilterPills = styled.div` &::-webkit-scrollbar { display: none; } - - mask-image: linear-gradient(to right, rgba(0, 0, 0, 1) 95%, rgba(255, 0, 0, 0.5) 100%, rgba(255, 0, 0, 0) 100%); `; type Props = { diff --git a/datahub-web-react/src/app/sharedV2/search/DownloadButton.tsx b/datahub-web-react/src/app/sharedV2/search/DownloadButton.tsx index 1ffcc53b85..d3bd16bd02 100644 --- a/datahub-web-react/src/app/sharedV2/search/DownloadButton.tsx +++ b/datahub-web-react/src/app/sharedV2/search/DownloadButton.tsx @@ -1,11 +1,9 @@ -import { DownloadOutlined } from '@ant-design/icons'; -import { Button, Tooltip } from '@components'; +import { Button, Tooltip, colors } from '@components'; import React from 'react'; import styled from 'styled-components'; const StyledButton = styled(Button)` - height: 28px; - margin: 0px 4px 0px 4px; + border: 1px solid ${colors.gray[100]}; `; type Props = { @@ -17,8 +15,16 @@ type Props = { export default function DownloadButton({ setShowDownloadAsCsvModal, isDownloadingCsv, disabled }: Props) { return ( - setShowDownloadAsCsvModal(true)} disabled={isDownloadingCsv || disabled}> - + setShowDownloadAsCsvModal(true)} + disabled={isDownloadingCsv || disabled} + isCircle + icon={{ icon: 'DownloadSimple', source: 'phosphor' }} + variant="text" + color="gray" + size="sm" + data-testid="download-csv-button" + > {isDownloadingCsv ? 'Downloading...' : null} diff --git a/datahub-web-react/src/app/sharedV2/search/EditButton.tsx b/datahub-web-react/src/app/sharedV2/search/EditButton.tsx index 22de955d2b..b4a5cc8e9b 100644 --- a/datahub-web-react/src/app/sharedV2/search/EditButton.tsx +++ b/datahub-web-react/src/app/sharedV2/search/EditButton.tsx @@ -1,11 +1,9 @@ -import { EditOutlined } from '@ant-design/icons'; -import { Button, Tooltip } from '@components'; +import { Button, Tooltip, colors } from '@components'; import React from 'react'; import styled from 'styled-components'; const StyledButton = styled(Button)` - height: 28px; - margin: 0px 4px 0px 4px; + border: 1px solid ${colors.gray[100]}; `; type Props = { @@ -20,9 +18,12 @@ export default function EditButton({ setShowSelectMode, disabled }: Props) { onClick={() => setShowSelectMode(true)} disabled={disabled} data-testid="search-results-edit-button" - > - - + isCircle + icon={{ icon: 'PencilSimple', source: 'phosphor' }} + variant="text" + color="gray" + size="sm" + /> ); } diff --git a/smoke-test/tests/cypress/cypress/e2e/lineageV2/v2_download_lineage_results.js b/smoke-test/tests/cypress/cypress/e2e/lineageV2/v2_download_lineage_results.js index 3b81c5abea..c79eba9b10 100644 --- a/smoke-test/tests/cypress/cypress/e2e/lineageV2/v2_download_lineage_results.js +++ b/smoke-test/tests/cypress/cypress/e2e/lineageV2/v2_download_lineage_results.js @@ -23,7 +23,7 @@ const third_degree_plus = [ ]; const downloadCsvFile = (filename) => { cy.get(".ant-list-items").should("be.visible"); - cy.get(".anticon-download").should("be.visible").click(); + cy.get('[data-testid="download-csv-button"]').should("be.visible").click(); cy.get('[data-testid="download-as-csv-input"]').clear().type(filename); cy.get('[data-testid="csv-modal-download-button"]').click().wait(5000); cy.ensureTextNotPresent("Creating CSV");