mirror of
https://github.com/datahub-project/datahub.git
synced 2025-12-28 18:38:17 +00:00
feat(react): show search suggestions (#2270)
This commit is contained in:
parent
183f7259c7
commit
aea35c1417
@ -161,6 +161,7 @@ const dataset2 = {
|
||||
};
|
||||
|
||||
const dataset3 = {
|
||||
__typename: 'Dataset',
|
||||
urn: 'urn:li:dataset:3',
|
||||
type: EntityType.Dataset,
|
||||
platform: {
|
||||
@ -237,6 +238,11 @@ const dataset3 = {
|
||||
deprecation: null,
|
||||
} as Dataset;
|
||||
|
||||
const dataset4 = {
|
||||
...dataset3,
|
||||
name: 'Fourth Test Dataset',
|
||||
};
|
||||
|
||||
const sampleTag = {
|
||||
urn: 'urn:li:tag:abc-sample-tag',
|
||||
name: 'abc-sample-tag',
|
||||
@ -755,6 +761,60 @@ export const mocks = [
|
||||
} as GetSearchResultsQuery,
|
||||
},
|
||||
},
|
||||
{
|
||||
request: {
|
||||
query: GetSearchResultsDocument,
|
||||
variables: {
|
||||
input: {
|
||||
type: 'DATASET',
|
||||
query: '*',
|
||||
start: 0,
|
||||
count: 20,
|
||||
filters: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
__typename: 'Query',
|
||||
search: {
|
||||
__typename: 'SearchResults',
|
||||
start: 0,
|
||||
count: 1,
|
||||
total: 1,
|
||||
entities: [
|
||||
{
|
||||
__typename: 'Dataset',
|
||||
...dataset3,
|
||||
},
|
||||
{
|
||||
__typename: 'Dataset',
|
||||
...dataset4,
|
||||
},
|
||||
],
|
||||
facets: [
|
||||
{
|
||||
field: 'origin',
|
||||
aggregations: [
|
||||
{
|
||||
value: 'PROD',
|
||||
count: 3,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
field: 'platform',
|
||||
aggregations: [
|
||||
{ value: 'hdfs', count: 1 },
|
||||
{ value: 'mysql', count: 1 },
|
||||
{ value: 'kafka', count: 1 },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
} as GetSearchResultsQuery,
|
||||
},
|
||||
},
|
||||
{
|
||||
request: {
|
||||
query: GetTagDocument,
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useHistory } from 'react-router';
|
||||
import { Typography, Image, Space, AutoComplete, Input, Row } from 'antd';
|
||||
import { Typography, Image, AutoComplete, Input, Row, Button, Carousel } from 'antd';
|
||||
import styled, { useTheme } from 'styled-components';
|
||||
|
||||
import { ManageAccount } from '../shared/ManageAccount';
|
||||
import { useGetAuthenticatedUser } from '../useGetAuthenticatedUser';
|
||||
import { useEntityRegistry } from '../useEntityRegistry';
|
||||
import { navigateToSearchUrl } from '../search/utils/navigateToSearchUrl';
|
||||
import { useGetAutoCompleteResultsLazyQuery } from '../../graphql/search.generated';
|
||||
import { GetSearchResultsQuery, useGetAutoCompleteResultsLazyQuery } from '../../graphql/search.generated';
|
||||
import { useGetAllEntitySearchResults } from '../../utils/customGraphQL/useGetAllEntitySearchResults';
|
||||
import { EntityType } from '../../types.generated';
|
||||
|
||||
const Background = styled(Space)`
|
||||
const Background = styled.div`
|
||||
width: 100%;
|
||||
background-image: linear-gradient(
|
||||
${(props) => props.theme.styles['homepage-background-upper-fade']},
|
||||
@ -25,12 +27,63 @@ const WelcomeText = styled(Typography.Text)`
|
||||
|
||||
const styles = {
|
||||
navBar: { padding: '24px' },
|
||||
searchContainer: { width: '100%', marginTop: '40px', marginBottom: '160px' },
|
||||
searchContainer: { width: '100%', marginTop: '40px' },
|
||||
logoImage: { width: 140 },
|
||||
searchBox: { width: 540, margin: '40px 0px' },
|
||||
subHeaderText: { color: '#FFFFFF', fontSize: 20 },
|
||||
subHeaderLabel: { marginTop: '-16px', color: '#FFFFFF', fontSize: 12 },
|
||||
};
|
||||
|
||||
const CarouselElement = styled.div`
|
||||
height: 120px;
|
||||
color: #fff;
|
||||
line-height: 120px;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const CarouselContainer = styled.div`
|
||||
margin-top: -24px;
|
||||
padding-bottom: 40px;
|
||||
`;
|
||||
|
||||
const HeaderContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
function getSuggestionFieldsFromResult(result: GetSearchResultsQuery): string[] {
|
||||
return (
|
||||
(result?.search?.entities
|
||||
?.map((entity) => {
|
||||
switch (entity.__typename) {
|
||||
case 'Dataset':
|
||||
return entity.name.split('.').slice(-1)[0];
|
||||
case 'CorpUser':
|
||||
return entity.username;
|
||||
case 'Chart':
|
||||
return entity.info?.name;
|
||||
case 'Dashboard':
|
||||
return entity.info?.name;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
})
|
||||
.filter(Boolean) as string[]) || []
|
||||
);
|
||||
}
|
||||
|
||||
function truncate(input, length) {
|
||||
if (input.length > length) {
|
||||
return `${input.substring(0, length)}...`;
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
function sortRandom() {
|
||||
return 0.5 - Math.random();
|
||||
}
|
||||
|
||||
export const HomePageHeader = () => {
|
||||
const history = useHistory();
|
||||
const entityRegistry = useEntityRegistry();
|
||||
@ -57,8 +110,37 @@ export const HomePageHeader = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// fetch some results from each entity to display search suggestions
|
||||
const allSearchResultsByType = useGetAllEntitySearchResults({
|
||||
query: '*',
|
||||
start: 0,
|
||||
count: 20,
|
||||
filters: [],
|
||||
});
|
||||
|
||||
const suggestionsLoading = Object.keys(allSearchResultsByType).some((type) => {
|
||||
return allSearchResultsByType[type].loading;
|
||||
});
|
||||
|
||||
const suggestionsToShow = useMemo(() => {
|
||||
let result: string[] = [];
|
||||
if (!suggestionsLoading) {
|
||||
[EntityType.Dashboard, EntityType.Chart, EntityType.Dataset].forEach((type) => {
|
||||
const suggestionsToShowForEntity = getSuggestionFieldsFromResult(
|
||||
allSearchResultsByType[type]?.data,
|
||||
).sort(sortRandom);
|
||||
const suggestionToAddToFront = suggestionsToShowForEntity?.pop();
|
||||
result = [...result, ...suggestionsToShowForEntity];
|
||||
if (suggestionToAddToFront) {
|
||||
result.splice(0, 0, suggestionToAddToFront);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}, [suggestionsLoading, allSearchResultsByType]);
|
||||
|
||||
return (
|
||||
<Background direction="vertical">
|
||||
<Background>
|
||||
<Row justify="space-between" style={styles.navBar}>
|
||||
<WelcomeText>
|
||||
{data && (
|
||||
@ -72,7 +154,7 @@ export const HomePageHeader = () => {
|
||||
pictureLink={data?.corpUser?.editableInfo?.pictureLink || ''}
|
||||
/>
|
||||
</Row>
|
||||
<Space direction="vertical" align="center" style={styles.searchContainer}>
|
||||
<HeaderContainer>
|
||||
<Image src={themeConfig.assets.logoUrl} preview={false} style={styles.logoImage} />
|
||||
<AutoComplete
|
||||
style={styles.searchBox}
|
||||
@ -88,11 +170,36 @@ export const HomePageHeader = () => {
|
||||
data-testid="search-input"
|
||||
/>
|
||||
</AutoComplete>
|
||||
|
||||
<Typography.Text style={styles.subHeaderText}>
|
||||
{themeConfig.content.homepage.homepageMessage}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
{suggestionsToShow.length === 0 && !suggestionsLoading && (
|
||||
<Typography.Text style={styles.subHeaderText}>
|
||||
{themeConfig.content.homepage.homepageMessage}
|
||||
</Typography.Text>
|
||||
)}
|
||||
<Typography.Text style={styles.subHeaderLabel}>Try searching for...</Typography.Text>
|
||||
</HeaderContainer>
|
||||
<CarouselContainer>
|
||||
<Carousel autoplay>
|
||||
{suggestionsToShow.length > 0 &&
|
||||
suggestionsToShow.slice(0, 3).map((suggestion) => (
|
||||
<CarouselElement>
|
||||
<Button
|
||||
type="text"
|
||||
style={styles.subHeaderText}
|
||||
onClick={() =>
|
||||
navigateToSearchUrl({
|
||||
type: undefined,
|
||||
query: suggestion,
|
||||
history,
|
||||
entityRegistry,
|
||||
})
|
||||
}
|
||||
>
|
||||
{truncate(suggestion, 40)}
|
||||
</Button>
|
||||
</CarouselElement>
|
||||
))}
|
||||
</Carousel>
|
||||
</CarouselContainer>
|
||||
</Background>
|
||||
);
|
||||
};
|
||||
|
||||
@ -41,4 +41,17 @@ describe('HomePage', () => {
|
||||
await waitFor(() => expect(queryByTitle('The Great Test Dataset')).toBeInTheDocument());
|
||||
await waitFor(() => expect(queryByTitle('Some other test')).toBeInTheDocument());
|
||||
});
|
||||
|
||||
it('renders search suggestions', async () => {
|
||||
const { getByText, queryAllByText } = render(
|
||||
<MockedProvider mocks={mocks} addTypename>
|
||||
<TestPageContainer>
|
||||
<HomePage />
|
||||
</TestPageContainer>
|
||||
</MockedProvider>,
|
||||
);
|
||||
await waitFor(() => expect(getByText('Try searching for...')).toBeInTheDocument());
|
||||
expect(queryAllByText('Yet Another Dataset').length).toBeGreaterThanOrEqual(1);
|
||||
expect(queryAllByText('Fourth Test Dataset').length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user