diff --git a/datahub-web-react/src/Mocks.tsx b/datahub-web-react/src/Mocks.tsx index 56e2fa1624..334d77097d 100644 --- a/datahub-web-react/src/Mocks.tsx +++ b/datahub-web-react/src/Mocks.tsx @@ -1774,6 +1774,14 @@ export const mocks = [ displayName: 'origin', aggregations: [{ value: 'PROD', count: 3, entity: null }], }, + { + field: 'entity', + displayName: 'Type', + aggregations: [ + { count: 37, entity: null, value: 'DATASET', __typename: 'AggregationMetadata' }, + { count: 7, entity: null, value: 'CHART', __typename: 'AggregationMetadata' }, + ], + }, { field: 'platform', displayName: 'platform', @@ -1837,6 +1845,14 @@ export const mocks = [ }, ], }, + { + field: 'entity', + displayName: 'Type', + aggregations: [ + { count: 37, entity: null, value: 'DATASET', __typename: 'AggregationMetadata' }, + { count: 7, entity: null, value: 'CHART', __typename: 'AggregationMetadata' }, + ], + }, { __typename: 'FacetMetadata', field: 'platform', @@ -1895,6 +1911,14 @@ export const mocks = [ }, ], }, + { + field: 'entity', + displayName: 'Type', + aggregations: [ + { count: 37, entity: null, value: 'DATASET', __typename: 'AggregationMetadata' }, + { count: 7, entity: null, value: 'CHART', __typename: 'AggregationMetadata' }, + ], + }, { field: 'platform', displayName: 'platform', @@ -1987,6 +2011,14 @@ export const mocks = [ }, ], }, + { + field: 'entity', + displayName: 'Type', + aggregations: [ + { count: 37, entity: null, value: 'DATASET', __typename: 'AggregationMetadata' }, + { count: 7, entity: null, value: 'CHART', __typename: 'AggregationMetadata' }, + ], + }, { field: 'platform', displayName: 'platform', @@ -2143,6 +2175,14 @@ export const mocks = [ }, ], }, + { + field: 'entity', + displayName: 'Type', + aggregations: [ + { count: 37, entity: null, value: 'DATASET', __typename: 'AggregationMetadata' }, + { count: 7, entity: null, value: 'CHART', __typename: 'AggregationMetadata' }, + ], + }, { field: 'platform', displayName: 'platform', @@ -2208,6 +2248,14 @@ export const mocks = [ }, ], }, + { + field: 'entity', + displayName: 'Type', + aggregations: [ + { count: 37, entity: null, value: 'DATASET', __typename: 'AggregationMetadata' }, + { count: 7, entity: null, value: 'CHART', __typename: 'AggregationMetadata' }, + ], + }, { field: 'platform', displayName: 'platform', @@ -2490,6 +2538,26 @@ export const mocks = [ }, ], }, + // { + // displayName: 'Domain', + // field: 'domains', + // __typename: 'FacetMetadata', + // aggregations: [ + // { + // value: 'urn:li:domain:baedb9f9-98ef-4846-8a0c-2a88680f213e', + // count: 1, + // __typename: 'AggregationMetadata', + // }, + // ], + // }, + { + field: 'entity', + displayName: 'Type', + aggregations: [ + { count: 37, entity: null, value: 'DATASET', __typename: 'AggregationMetadata' }, + { count: 7, entity: null, value: 'CHART', __typename: 'AggregationMetadata' }, + ], + }, { __typename: 'FacetMetadata', field: 'platform', @@ -2665,6 +2733,14 @@ export const mocks = [ }, ], }, + { + field: 'entity', + displayName: 'Type', + aggregations: [ + { count: 37, entity: null, value: 'DATASET', __typename: 'AggregationMetadata' }, + { count: 7, entity: null, value: 'CHART', __typename: 'AggregationMetadata' }, + ], + }, { field: 'platform', displayName: 'platform', @@ -2731,69 +2807,11 @@ export const mocks = [ ], }, { - field: 'platform', - displayName: 'platform', + field: 'entity', + displayName: 'Type', aggregations: [ - { value: 'hdfs', count: 1, entity: null }, - { value: 'mysql', count: 1, entity: null }, - { value: 'kafka', count: 1, entity: null }, - ], - }, - ], - }, - } as GetSearchResultsForMultipleQuery, - }, - }, - { - request: { - query: GetSearchResultsForMultipleDocument, - variables: { - input: { - types: ['DATASET'], - query: 'test', - start: 0, - count: 10, - filters: [ - { - field: 'platform', - value: 'kafka', - }, - { - field: 'platform', - value: 'hdfs', - }, - ], - }, - }, - }, - result: { - data: { - __typename: 'Query', - searchAcrossEntities: { - __typename: 'SearchResults', - start: 0, - count: 1, - total: 1, - searchResults: [ - { - entity: { - __typename: 'Dataset', - ...dataset3, - }, - matchedFields: [], - insights: [], - }, - ], - facets: [ - { - field: 'origin', - displayName: 'origin', - aggregations: [ - { - value: 'PROD', - count: 3, - entity: null, - }, + { count: 37, entity: null, value: 'DATASET', __typename: 'AggregationMetadata' }, + { count: 7, entity: null, value: 'CHART', __typename: 'AggregationMetadata' }, ], }, { @@ -2862,6 +2880,88 @@ export const mocks = [ }, ], }, + { + field: 'entity', + displayName: 'Type', + aggregations: [ + { count: 37, entity: null, value: 'DATASET', __typename: 'AggregationMetadata' }, + { count: 7, entity: null, value: 'CHART', __typename: 'AggregationMetadata' }, + ], + }, + { + field: 'platform', + displayName: 'platform', + aggregations: [ + { value: 'hdfs', count: 1, entity: null }, + { value: 'mysql', count: 1, entity: null }, + { value: 'kafka', count: 1, entity: null }, + ], + }, + ], + }, + } as GetSearchResultsForMultipleQuery, + }, + }, + { + request: { + query: GetSearchResultsForMultipleDocument, + variables: { + input: { + types: ['DATASET'], + query: 'test', + start: 0, + count: 10, + filters: [ + { + field: 'platform', + value: 'kafka', + }, + { + field: 'platform', + value: 'hdfs', + }, + ], + }, + }, + }, + result: { + data: { + __typename: 'Query', + searchAcrossEntities: { + __typename: 'SearchResults', + start: 0, + count: 1, + total: 1, + searchResults: [ + { + entity: { + __typename: 'Dataset', + ...dataset3, + }, + matchedFields: [], + insights: [], + }, + ], + facets: [ + { + field: 'origin', + displayName: 'origin', + aggregations: [ + { + value: 'PROD', + count: 3, + entity: null, + }, + ], + }, + { + field: 'entity', + displayName: 'Type', + aggregations: [ + { count: 37, entity: null, value: 'DATASET', __typename: 'AggregationMetadata' }, + { count: 7, entity: null, value: 'CHART', __typename: 'AggregationMetadata' }, + ], + }, { field: 'platform', displayName: 'platform', @@ -3096,6 +3196,14 @@ export const mocks = [ }, ], }, + { + field: 'entity', + displayName: 'Type', + aggregations: [ + { count: 37, entity: null, value: 'DATASET', __typename: 'AggregationMetadata' }, + { count: 7, entity: null, value: 'CHART', __typename: 'AggregationMetadata' }, + ], + }, { field: 'platform', displayName: 'platform', diff --git a/datahub-web-react/src/app/entity/dataset/profile/UsageFacepile.tsx b/datahub-web-react/src/app/entity/dataset/profile/UsageFacepile.tsx index e930dcb99d..0629e0128e 100644 --- a/datahub-web-react/src/app/entity/dataset/profile/UsageFacepile.tsx +++ b/datahub-web-react/src/app/entity/dataset/profile/UsageFacepile.tsx @@ -8,6 +8,7 @@ import getAvatarColor from '../../../shared/avatar/getAvatarColor'; export type Props = { users?: (UserUsageCounts | null)[] | null; + maxNumberDisplayed?: number; }; const AvatarStyled = styled(Avatar)<{ backgroundColor: string }>` @@ -15,12 +16,16 @@ const AvatarStyled = styled(Avatar)<{ backgroundColor: string }>` background-color: ${(props) => props.backgroundColor}; `; -export default function UsageFacepile({ users }: Props) { +export default function UsageFacepile({ users, maxNumberDisplayed }: Props) { const sortedUsers = useMemo(() => users?.slice().sort((a, b) => (b?.count || 0) - (a?.count || 0)), [users]); + let displayedUsers = sortedUsers; + if (maxNumberDisplayed) { + displayedUsers = displayedUsers?.slice(0, maxNumberDisplayed); + } return ( - {sortedUsers?.map((user) => ( + {displayedUsers?.map((user) => ( {user?.userEmail?.charAt(0).toUpperCase()} diff --git a/datahub-web-react/src/app/entity/shared/containers/profile/sidebar/Dataset/StatsSidebarSection.tsx b/datahub-web-react/src/app/entity/shared/containers/profile/sidebar/Dataset/StatsSidebarSection.tsx index f7a7443756..3ba08d9b36 100644 --- a/datahub-web-react/src/app/entity/shared/containers/profile/sidebar/Dataset/StatsSidebarSection.tsx +++ b/datahub-web-react/src/app/entity/shared/containers/profile/sidebar/Dataset/StatsSidebarSection.tsx @@ -100,7 +100,7 @@ export const SidebarStatsSection = () => { ) : null} {(usageStats?.aggregations?.users?.length || 0) > 0 ? ( - + ) : null} diff --git a/datahub-web-react/src/app/entity/shared/tabs/Dataset/Stats/snapshot/TableStats.tsx b/datahub-web-react/src/app/entity/shared/tabs/Dataset/Stats/snapshot/TableStats.tsx index f66fbd308c..31ee3ded96 100644 --- a/datahub-web-react/src/app/entity/shared/tabs/Dataset/Stats/snapshot/TableStats.tsx +++ b/datahub-web-react/src/app/entity/shared/tabs/Dataset/Stats/snapshot/TableStats.tsx @@ -70,7 +70,7 @@ export default function TableStats({ {users && (
- +
)} diff --git a/datahub-web-react/src/app/home/HomePageHeader.tsx b/datahub-web-react/src/app/home/HomePageHeader.tsx index f6ed18fdcc..1bcb68dd90 100644 --- a/datahub-web-react/src/app/home/HomePageHeader.tsx +++ b/datahub-web-react/src/app/home/HomePageHeader.tsx @@ -38,7 +38,7 @@ const styles = { navBar: { padding: '24px' }, searchContainer: { width: '100%', marginTop: '40px' }, logoImage: { width: 140 }, - searchBox: { width: '40vw', minWidth: 400, margin: '40px 0px', marginBottom: '12px' }, + searchBox: { width: '47vw', minWidth: 400, margin: '40px 0px', marginBottom: '12px', maxWidth: '650px' }, subtitle: { marginTop: '28px', color: '#FFFFFF', fontSize: 12 }, }; @@ -58,8 +58,9 @@ const NavGroup = styled.div` `; const SuggestionsContainer = styled.div` - padding: 0px 30px; - max-width: 540px; + margin: 0px 30px; + max-width: 650px; + width: 47vw; display: flex; flex-direction: column; justify-content: left; diff --git a/datahub-web-react/src/app/lineage/LineageEntityNode.tsx b/datahub-web-react/src/app/lineage/LineageEntityNode.tsx index 610d8acbb6..c888d72bf5 100644 --- a/datahub-web-react/src/app/lineage/LineageEntityNode.tsx +++ b/datahub-web-react/src/app/lineage/LineageEntityNode.tsx @@ -1,15 +1,16 @@ -import React, { useContext, useMemo, useState } from 'react'; +import React, { useContext, useEffect, useMemo, useState } from 'react'; import { Group } from '@vx/group'; import { LinkHorizontal } from '@vx/shape'; import styled from 'styled-components'; import { useEntityRegistry } from '../useEntityRegistry'; import { IconStyleType } from '../entity/Entity'; -import { NodeData, Direction, VizNode, EntitySelectParams } from './types'; +import { NodeData, Direction, VizNode, EntitySelectParams, EntityAndType } from './types'; import { ANTD_GRAY } from '../entity/shared/constants'; import { capitalizeFirstLetter } from '../shared/textUtil'; import { nodeHeightFromTitleLength } from './utils/nodeHeightFromTitleLength'; import { LineageExplorerContext } from './utils/LineageExplorerContext'; +import useLazyGetEntityQuery from './utils/useLazyGetEntityQuery'; const CLICK_DELAY_THRESHOLD = 1000; const DRAG_DISTANCE_THRESHOLD = 20; @@ -81,13 +82,20 @@ export default function LineageEntityNode({ onEntityCenter: (EntitySelectParams) => void; onHover: (EntitySelectParams) => void; onDrag: (params: EntitySelectParams, event: React.MouseEvent) => void; - onExpandClick: (LineageExpandParams) => void; + onExpandClick: (data: EntityAndType) => void; direction: Direction; nodesToRenderByUrn: Record; }) { const { expandTitles } = useContext(LineageExplorerContext); const [isExpanding, setIsExpanding] = useState(false); const [expandHover, setExpandHover] = useState(false); + const { getAsyncEntity, asyncData } = useLazyGetEntityQuery(); + + useEffect(() => { + if (asyncData) { + onExpandClick(asyncData); + } + }, [asyncData, onExpandClick]); const entityRegistry = useEntityRegistry(); const unexploredHiddenChildren = @@ -139,7 +147,9 @@ export default function LineageEntityNode({ { setIsExpanding(true); - onExpandClick({ urn: node.data.urn, type: node.data.type, direction }); + if (node.data.urn && node.data.type) { + getAsyncEntity(node.data.urn, node.data.type); + } }} onMouseOver={() => { setExpandHover(true); diff --git a/datahub-web-react/src/app/lineage/LineageExplorer.tsx b/datahub-web-react/src/app/lineage/LineageExplorer.tsx index d248742ec6..aedba2e1fe 100644 --- a/datahub-web-react/src/app/lineage/LineageExplorer.tsx +++ b/datahub-web-react/src/app/lineage/LineageExplorer.tsx @@ -8,10 +8,9 @@ import styled from 'styled-components'; import { Message } from '../shared/Message'; import { useEntityRegistry } from '../useEntityRegistry'; import CompactContext from '../shared/CompactContext'; -import { EntityAndType, EntitySelectParams, FetchedEntities, LineageExpandParams } from './types'; +import { EntityAndType, EntitySelectParams, FetchedEntities } from './types'; import LineageViz from './LineageViz'; import extendAsyncEntities from './utils/extendAsyncEntities'; -import useLazyGetEntityQuery from './utils/useLazyGetEntityQuery'; import useGetEntityQuery from './utils/useGetEntityQuery'; import { EntityType } from '../../types.generated'; import { capitalizeFirstLetter } from '../shared/textUtil'; @@ -58,7 +57,6 @@ export default function LineageExplorer({ urn, type }: Props) { const entityRegistry = useEntityRegistry(); const { loading, error, data } = useGetEntityQuery(urn, type); - const { getAsyncEntity, asyncData } = useLazyGetEntityQuery(); const [isDrawerVisible, setIsDrawVisible] = useState(false); const [selectedEntity, setSelectedEntity] = useState(undefined); @@ -94,10 +92,7 @@ export default function LineageExplorer({ urn, type }: Props) { if (type && data) { maybeAddAsyncLoadedEntity(data); } - if (asyncData) { - maybeAddAsyncLoadedEntity(asyncData); - } - }, [data, asyncData, asyncEntities, setAsyncEntities, maybeAddAsyncLoadedEntity, urn, previousUrn, type]); + }, [data, asyncEntities, setAsyncEntities, maybeAddAsyncLoadedEntity, urn, previousUrn, type]); if (error || (!loading && !error && !data)) { return ; @@ -124,8 +119,8 @@ export default function LineageExplorer({ urn, type }: Props) { `${entityRegistry.getEntityUrl(params.type, params.urn)}/?is_lineage_mode=true`, ); }} - onLineageExpand={(params: LineageExpandParams) => { - getAsyncEntity(params.urn, params.type); + onLineageExpand={(asyncData: EntityAndType) => { + maybeAddAsyncLoadedEntity(asyncData); }} /> diff --git a/datahub-web-react/src/app/lineage/LineageTree.tsx b/datahub-web-react/src/app/lineage/LineageTree.tsx index 207d8a0a58..402806741c 100644 --- a/datahub-web-react/src/app/lineage/LineageTree.tsx +++ b/datahub-web-react/src/app/lineage/LineageTree.tsx @@ -1,7 +1,7 @@ import React, { useContext, useEffect, useMemo, useState } from 'react'; import { TransformMatrix } from '@vx/zoom/lib/types'; -import { NodeData, Direction, EntitySelectParams, TreeProps } from './types'; +import { NodeData, Direction, EntitySelectParams, TreeProps, EntityAndType } from './types'; import LineageTreeNodeAndEdgeRenderer from './LineageTreeNodeAndEdgeRenderer'; import layoutTree from './utils/layoutTree'; import { LineageExplorerContext } from './utils/LineageExplorerContext'; @@ -13,7 +13,7 @@ type LineageTreeProps = { }; onEntityClick: (EntitySelectParams) => void; onEntityCenter: (EntitySelectParams) => void; - onLineageExpand: (LineageExpandParams) => void; + onLineageExpand: (data: EntityAndType) => void; selectedEntity?: EntitySelectParams; hoveredEntity?: EntitySelectParams; setHoveredEntity: (EntitySelectParams) => void; diff --git a/datahub-web-react/src/app/lineage/LineageTreeNodeAndEdgeRenderer.tsx b/datahub-web-react/src/app/lineage/LineageTreeNodeAndEdgeRenderer.tsx index eec8913708..0920b83477 100644 --- a/datahub-web-react/src/app/lineage/LineageTreeNodeAndEdgeRenderer.tsx +++ b/datahub-web-react/src/app/lineage/LineageTreeNodeAndEdgeRenderer.tsx @@ -4,7 +4,7 @@ import { curveBasis } from '@vx/curve'; import { LinePath } from '@vx/shape'; import { TransformMatrix } from '@vx/zoom/lib/types'; -import { NodeData, Direction, EntitySelectParams, TreeProps, VizNode, VizEdge } from './types'; +import { NodeData, Direction, EntitySelectParams, TreeProps, VizNode, VizEdge, EntityAndType } from './types'; import LineageEntityNode from './LineageEntityNode'; import { ANTD_GRAY } from '../entity/shared/constants'; @@ -15,7 +15,7 @@ type Props = { }; onEntityClick: (EntitySelectParams) => void; onEntityCenter: (EntitySelectParams) => void; - onLineageExpand: (LineageExpandParams) => void; + onLineageExpand: (data: EntityAndType) => void; selectedEntity?: EntitySelectParams; hoveredEntity?: EntitySelectParams; setHoveredEntity: (EntitySelectParams) => void; diff --git a/datahub-web-react/src/app/lineage/LineageVizInsideZoom.tsx b/datahub-web-react/src/app/lineage/LineageVizInsideZoom.tsx index 00a59052ce..ca8987b7e6 100644 --- a/datahub-web-react/src/app/lineage/LineageVizInsideZoom.tsx +++ b/datahub-web-react/src/app/lineage/LineageVizInsideZoom.tsx @@ -68,7 +68,7 @@ type Props = { fetchedEntities: { [x: string]: FetchedEntity }; onEntityClick: (EntitySelectParams) => void; onEntityCenter: (EntitySelectParams) => void; - onLineageExpand: (LineageExpandParams) => void; + onLineageExpand: (data: EntityAndType) => void; selectedEntity?: EntitySelectParams; zoom: ProvidedZoom & { transformMatrix: TransformMatrix; diff --git a/datahub-web-react/src/app/lineage/types.ts b/datahub-web-react/src/app/lineage/types.ts index b6365d1104..4a955c5e8c 100644 --- a/datahub-web-react/src/app/lineage/types.ts +++ b/datahub-web-react/src/app/lineage/types.ts @@ -87,7 +87,7 @@ export type TreeProps = { fetchedEntities: { [x: string]: FetchedEntity }; onEntityClick: (EntitySelectParams) => void; onEntityCenter: (EntitySelectParams) => void; - onLineageExpand: (LineageExpandParams) => void; + onLineageExpand: (data: EntityAndType) => void; selectedEntity?: EntitySelectParams; hoveredEntity?: EntitySelectParams; }; diff --git a/datahub-web-react/src/app/search/SearchBar.tsx b/datahub-web-react/src/app/search/SearchBar.tsx index 6c2f5f69c5..5dda18ff83 100644 --- a/datahub-web-react/src/app/search/SearchBar.tsx +++ b/datahub-web-react/src/app/search/SearchBar.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState, useRef } from 'react'; import { Input, AutoComplete, Image, Typography } from 'antd'; import { SearchOutlined } from '@ant-design/icons'; import styled from 'styled-components'; @@ -37,7 +37,7 @@ const ExploreForEntity = styled.span` const StyledAutoComplete = styled(AutoComplete)` width: 100%; - max-width: 475px; + max-width: 650px; `; const AutoCompleteContainer = styled.div` @@ -161,6 +161,7 @@ interface Props { autoCompleteStyle?: React.CSSProperties; entityRegistry: EntityRegistry; fixAutoComplete?: boolean; + setIsSearchBarFocused?: (isSearchBarFocused: boolean) => void; } const defaultProps = { @@ -181,6 +182,7 @@ export const SearchBar = ({ inputStyle, autoCompleteStyle, fixAutoComplete, + setIsSearchBarFocused, }: Props) => { const history = useHistory(); const [searchQuery, setSearchQuery] = useState(); @@ -249,8 +251,20 @@ export const SearchBar = ({ return emptyQueryOptions; }, [emptyQueryOptions, autoCompleteEntityOptions, autoCompleteQueryOptions]); + const searchBarWrapperRef = useRef(null); + + function handleSearchBarClick(isSearchBarFocused: boolean) { + if ( + setIsSearchBarFocused && + (!isSearchBarFocused || + (searchBarWrapperRef && searchBarWrapperRef.current && searchBarWrapperRef.current.clientWidth < 590)) + ) { + setIsSearchBarFocused(isSearchBarFocused); + } + } + return ( - + setSearchQuery(e.target.value)} data-testid="search-input" prefix={ onSearch(filterSearchQuery(searchQuery || ''))} />} + onFocus={() => handleSearchBarClick(true)} + onBlur={() => handleSearchBarClick(false)} /> diff --git a/datahub-web-react/src/app/search/SearchFilter.tsx b/datahub-web-react/src/app/search/SearchFilter.tsx index 6ec191990e..34cbb49ea0 100644 --- a/datahub-web-react/src/app/search/SearchFilter.tsx +++ b/datahub-web-react/src/app/search/SearchFilter.tsx @@ -1,3 +1,4 @@ +import { DownOutlined, UpOutlined } from '@ant-design/icons'; import { Button, Checkbox } from 'antd'; import { CheckboxChangeEvent } from 'antd/lib/checkbox'; import * as React from 'react'; @@ -6,7 +7,7 @@ import styled from 'styled-components'; import { FacetMetadata } from '../../types.generated'; import { SearchFilterLabel } from './SearchFilterLabel'; -import { FILTERS_TO_TRUNCATE, TRUNCATED_FILTER_LENGTH } from './utils/constants'; +import { TRUNCATED_FILTER_LENGTH } from './utils/constants'; type Props = { facet: FacetMetadata; @@ -15,6 +16,7 @@ type Props = { value: string; }>; onFilterSelect: (selected: boolean, field: string, value: string) => void; + defaultDisplayFilters: boolean; }; const SearchFilterWrapper = styled.div` @@ -22,8 +24,11 @@ const SearchFilterWrapper = styled.div` `; const Title = styled.div` + align-items: center; font-weight: bold; margin-bottom: 10px; + display: flex; + justify-content: space-between; `; const CheckBox = styled(Checkbox)` @@ -37,41 +42,63 @@ const ExpandButton = styled(Button)` } `; -export const SearchFilter = ({ facet, selectedFilters, onFilterSelect }: Props) => { +const StyledUpOutlined = styled(UpOutlined)` + font-size: 10px; +`; + +const StyledDownOutlined = styled(DownOutlined)` + font-size: 10px; +`; + +export const SearchFilter = ({ facet, selectedFilters, onFilterSelect, defaultDisplayFilters }: Props) => { + const [areFiltersVisible, setAreFiltersVisible] = useState(defaultDisplayFilters); const [expanded, setExpanded] = useState(false); - const shouldTruncate = - FILTERS_TO_TRUNCATE.indexOf(facet.field) > -1 && facet.aggregations.length > TRUNCATED_FILTER_LENGTH; + const shouldTruncate = facet.aggregations.length > TRUNCATED_FILTER_LENGTH; return ( - {facet?.displayName} - {facet.aggregations.map((aggregation, i) => { - if (i >= TRUNCATED_FILTER_LENGTH && !expanded && shouldTruncate) { - return null; - } - return ( - - f.field === facet.field && f.value === aggregation.value, - ) !== undefined - } - onChange={(e: CheckboxChangeEvent) => - onFilterSelect(e.target.checked, facet.field, aggregation.value) - } - > - - -
-
- ); - })} - {shouldTruncate && ( - setExpanded(!expanded)}> - {expanded ? '- Less' : '+ More'} - + + {facet?.displayName} + {areFiltersVisible ? ( + <StyledUpOutlined onClick={() => setAreFiltersVisible(false)} /> + ) : ( + <StyledDownOutlined + data-testid={`expand-facet-${facet.field}`} + onClick={() => setAreFiltersVisible(true)} + /> + )} + + {areFiltersVisible && ( + <> + {facet.aggregations.map((aggregation, i) => { + if (i >= TRUNCATED_FILTER_LENGTH && !expanded) { + return null; + } + return ( + + f.field === facet.field && f.value === aggregation.value, + ) !== undefined + } + onChange={(e: CheckboxChangeEvent) => + onFilterSelect(e.target.checked, facet.field, aggregation.value) + } + > + + +
+
+ ); + })} + {shouldTruncate && ( + setExpanded(!expanded)}> + {expanded ? '- Less' : '+ More'} + + )} + )}
); diff --git a/datahub-web-react/src/app/search/SearchFilters.tsx b/datahub-web-react/src/app/search/SearchFilters.tsx index 392960d373..5cec5336b2 100644 --- a/datahub-web-react/src/app/search/SearchFilters.tsx +++ b/datahub-web-react/src/app/search/SearchFilters.tsx @@ -4,6 +4,8 @@ import { useEffect, useState } from 'react'; import { FacetMetadata } from '../../types.generated'; import { SearchFilter } from './SearchFilter'; +const TOP_FILTERS = ['entity', 'tags', 'glossaryTerms', 'domains', 'owners']; + export const SearchFilterWrapper = styled.div` max-height: 100%; overflow: auto; @@ -62,14 +64,21 @@ export const SearchFilters = ({ facets, selectedFilters, onFilterSelect, loading onFilterSelect(newFilters); }; + const sortedFacets = cachedProps.facets.sort((facetA, facetB) => { + if (TOP_FILTERS.indexOf(facetA.field) === -1) return 1; + if (TOP_FILTERS.indexOf(facetB.field) === -1) return -1; + return TOP_FILTERS.indexOf(facetA.field) - TOP_FILTERS.indexOf(facetB.field); + }); + return ( - {cachedProps.facets.map((facet) => ( + {sortedFacets.map((facet) => ( ))} diff --git a/datahub-web-react/src/app/search/SearchHeader.tsx b/datahub-web-react/src/app/search/SearchHeader.tsx index 465f7082e9..2214532cb6 100644 --- a/datahub-web-react/src/app/search/SearchHeader.tsx +++ b/datahub-web-react/src/app/search/SearchHeader.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import React, { useState } from 'react'; import { Image, Layout } from 'antd'; import { Link } from 'react-router-dom'; import styled, { useTheme } from 'styled-components'; @@ -75,6 +75,7 @@ export const SearchHeader = ({ authenticatedUserPictureLink, entityRegistry, }: Props) => { + const [isSearchBarFocused, setIsSearchBarFocused] = useState(false); const themeConfig = useTheme(); const appConfig = useAppConfig(); @@ -98,11 +99,12 @@ export const SearchHeader = ({ onSearch={onSearch} onQueryChange={onQueryChange} entityRegistry={entityRegistry} + setIsSearchBarFocused={setIsSearchBarFocused} fixAutoComplete /> - + diff --git a/datahub-web-react/src/app/search/__tests__/SearchPage.test.tsx b/datahub-web-react/src/app/search/__tests__/SearchPage.test.tsx index 938aa69747..4a2ca7c789 100644 --- a/datahub-web-react/src/app/search/__tests__/SearchPage.test.tsx +++ b/datahub-web-react/src/app/search/__tests__/SearchPage.test.tsx @@ -26,7 +26,6 @@ describe('SearchPage', () => { }); it('renders the selected filters as checked', async () => { - const promise = Promise.resolve(); const { getByTestId, queryByTestId } = render( { , ); - await waitFor(() => expect(queryByTestId('facet-platform-kafka')).toBeInTheDocument()); + await waitFor(() => expect(queryByTestId('facet-entity-DATASET')).toBeInTheDocument()); - const kafkaPlatformBox = getByTestId('facet-platform-kafka'); - expect(kafkaPlatformBox).toHaveProperty('checked', true); + const datasetEntityBox = getByTestId('facet-entity-DATASET'); + expect(datasetEntityBox).toHaveProperty('checked', true); - const hdfsPlatformBox = getByTestId('facet-platform-hdfs'); - expect(hdfsPlatformBox).toHaveProperty('checked', false); - - const prodOriginBox = getByTestId('facet-origin-PROD'); - expect(prodOriginBox).toHaveProperty('checked', false); - await act(() => promise); + const chartEntityBox = getByTestId('facet-entity-CHART'); + expect(chartEntityBox).toHaveProperty('checked', false); }); it('renders multiple checked filters at once', async () => { - const promise = Promise.resolve(); const { getByTestId, queryByTestId } = render( { , ); - await waitFor(() => expect(queryByTestId('facet-platform-kafka')).toBeInTheDocument()); + await waitFor(() => expect(queryByTestId('facet-entity-DATASET')).toBeInTheDocument()); - const kafkaPlatformBox = getByTestId('facet-platform-kafka'); - expect(kafkaPlatformBox).toHaveProperty('checked', true); + const datasetEntityBox = getByTestId('facet-entity-DATASET'); + expect(datasetEntityBox).toHaveProperty('checked', true); + const expandButton = getByTestId('expand-facet-platform'); + act(() => { + fireEvent.click(expandButton); + }); + + await waitFor(() => expect(queryByTestId('facet-platform-hdfs')).toBeInTheDocument()); const hdfsPlatformBox = getByTestId('facet-platform-hdfs'); expect(hdfsPlatformBox).toHaveProperty('checked', true); - - const prodOriginBox = getByTestId('facet-origin-PROD'); - expect(prodOriginBox).toHaveProperty('checked', false); - await act(() => promise); }); it('clicking a filter selects a new filter', async () => { @@ -108,24 +104,24 @@ describe('SearchPage', () => { , ); - await waitFor(() => expect(queryByTestId('facet-platform-kafka')).toBeInTheDocument()); + await waitFor(() => expect(queryByTestId('facet-entity-DATASET')).toBeInTheDocument()); - const kafkaPlatformBox = getByTestId('facet-platform-kafka'); - expect(kafkaPlatformBox).toHaveProperty('checked', true); + const datasetEntityBox = getByTestId('facet-entity-DATASET'); + expect(datasetEntityBox).toHaveProperty('checked', true); - const hdfsPlatformBox = getByTestId('facet-platform-hdfs'); - expect(hdfsPlatformBox).toHaveProperty('checked', false); + const chartEntityBox = getByTestId('facet-entity-CHART'); + expect(chartEntityBox).toHaveProperty('checked', false); act(() => { - fireEvent.click(hdfsPlatformBox); + fireEvent.click(chartEntityBox); }); - await waitFor(() => expect(queryByTestId('facet-platform-kafka')).toBeInTheDocument()); + await waitFor(() => expect(queryByTestId('facet-entity-DATASET')).toBeInTheDocument()); - const kafkaPlatformBox2 = getByTestId('facet-platform-kafka'); - expect(kafkaPlatformBox2).toHaveProperty('checked', true); + const datasetEntityBox2 = getByTestId('facet-entity-DATASET'); + expect(datasetEntityBox2).toHaveProperty('checked', true); - const hdfsPlatformBox2 = getByTestId('facet-platform-hdfs'); - expect(hdfsPlatformBox2).toHaveProperty('checked', true); + const chartEntityBox2 = getByTestId('facet-entity-CHART'); + expect(chartEntityBox2).toHaveProperty('checked', true); await act(() => promise); }); }); diff --git a/datahub-web-react/src/app/shared/ManageAccount.tsx b/datahub-web-react/src/app/shared/ManageAccount.tsx index e6890437a0..551e78f217 100644 --- a/datahub-web-react/src/app/shared/ManageAccount.tsx +++ b/datahub-web-react/src/app/shared/ManageAccount.tsx @@ -32,6 +32,10 @@ const DownArrow = styled(CaretDownOutlined)` color: ${ANTD_GRAY[7]}; `; +const StyledLink = styled(Link)` + white-space: nowrap; +`; + interface Props { urn: string; pictureLink?: string; @@ -85,10 +89,10 @@ export const ManageAccount = ({ urn: _urn, pictureLink: _pictureLink, name }: Pr return ( - + - + ); }; diff --git a/datahub-web-react/src/app/shared/admin/AdminHeaderLinks.tsx b/datahub-web-react/src/app/shared/admin/AdminHeaderLinks.tsx index 672114f395..31cbbc9427 100644 --- a/datahub-web-react/src/app/shared/admin/AdminHeaderLinks.tsx +++ b/datahub-web-react/src/app/shared/admin/AdminHeaderLinks.tsx @@ -17,7 +17,25 @@ const AdminLink = styled.span` margin-right: 4px; `; -export function AdminHeaderLinks() { +const LinksWrapper = styled.div<{ areLinksHidden?: boolean }>` + opacity: 1; + white-space: nowrap; + transition: opacity 0.5s; + + ${(props) => + props.areLinksHidden && + ` + opacity: 0; + width: 0; + `} +`; + +interface Props { + areLinksHidden?: boolean; +} + +export function AdminHeaderLinks(props: Props) { + const { areLinksHidden } = props; const me = useGetAuthenticatedUser(); const { config } = useAppConfig(); @@ -36,7 +54,7 @@ export function AdminHeaderLinks() { const showDomains = me?.platformPrivileges?.manageDomains || false; return ( - <> + {showAnalytics && ( @@ -91,6 +109,6 @@ export function AdminHeaderLinks() { )} - + ); }