refactor(react): All entity search UI + misc improvements (#2233)

Co-authored-by: John Joyce <john@acryl.io>
This commit is contained in:
John Joyce 2021-03-13 07:55:29 -08:00 committed by GitHub
parent f8a7f1ef96
commit 595d3672d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 93 additions and 71 deletions

View File

@ -2,6 +2,7 @@ import React from 'react';
import { useParams } from 'react-router-dom';
import { EntityType } from '../../types.generated';
import { BrowsableEntityPage } from '../browse/BrowsableEntityPage';
import { SearchablePage } from '../search/SearchablePage';
import { useEntityRegistry } from '../useEntityRegistry';
interface RouteParams {
@ -18,10 +19,11 @@ interface Props {
export const EntityPage = ({ entityType }: Props) => {
const { urn } = useParams<RouteParams>();
const entityRegistry = useEntityRegistry();
const isBrowsable = entityRegistry.getEntity(entityType).isBrowseEnabled();
const ContainerPage = isBrowsable ? BrowsableEntityPage : SearchablePage;
return (
<BrowsableEntityPage urn={urn} type={entityType}>
<ContainerPage urn={urn} type={entityType}>
{entityRegistry.renderProfile(entityType, urn)}
</BrowsableEntityPage>
</ContainerPage>
);
};

View File

@ -27,6 +27,10 @@ export default class EntityRegistry {
this.pathNameToEntityType.set(entity.getPathName(), entity.type);
}
getEntity(type: EntityType): Entity<any> {
return validatedGet(type, this.entityTypeToEntity);
}
getEntities(): Array<Entity<any>> {
return this.entities;
}

View File

@ -27,6 +27,8 @@ const styles = {
typeName: { color: '#585858' },
platformName: { color: '#585858' },
ownedBy: { color: '#585858' },
description: { paddingLeft: 8, margin: 0 },
noDescription: { color: '#d8d8d8' },
};
export default function DefaultPreviewCard({
@ -45,7 +47,7 @@ export default function DefaultPreviewCard({
<Row style={styles.row} justify="space-between">
<Space direction="vertical" align="start" size={28} style={styles.leftColumn}>
<Link to={url}>
<Space direction="horizontal" size={28} align="center">
<Space direction="horizontal" size={20} align="center">
{logoUrl && <Image style={styles.logoImage} src={logoUrl} preview />}
<Space direction="vertical" size={8}>
<Typography.Text strong style={styles.name}>
@ -59,12 +61,20 @@ export default function DefaultPreviewCard({
</Space>
</Space>
</Link>
<Typography.Paragraph>{description}</Typography.Paragraph>
{description.length === 0 ? (
<Typography.Paragraph style={{ ...styles.description, ...styles.noDescription }}>
No description
</Typography.Paragraph>
) : (
<Typography.Paragraph style={styles.description}>{description}</Typography.Paragraph>
)}
</Space>
<Space direction="vertical" align="end" size={36} style={styles.rightColumn}>
<Space>
<TagGroup globalTags={tags} />
</Space>
{tags && tags.tags?.length && (
<Space>
<TagGroup globalTags={tags} />
</Space>
)}
<Space direction="vertical" size={12}>
<Typography.Text strong>Owned By</Typography.Text>
<Avatar.Group maxCount={4}>

View File

@ -0,0 +1,46 @@
import * as React from 'react';
import { List } from 'antd';
import { useGetAllEntitySearchResults } from '../../utils/customGraphQL/useGetAllEntitySearchResults';
import { Message } from '../shared/Message';
import { EntityGroupSearchResults } from './EntityGroupSearchResults';
interface Props {
query: string;
}
const RESULTS_PER_GROUP = 3;
export const AllEntitiesSearchResults = ({ query }: Props) => {
const allSearchResultsByType = useGetAllEntitySearchResults({
query,
start: 0,
count: RESULTS_PER_GROUP,
filters: null,
});
const loading = Object.keys(allSearchResultsByType).some((type) => {
return allSearchResultsByType[type].loading;
});
const noResults = Object.keys(allSearchResultsByType).every((type) => {
return (
!allSearchResultsByType[type].loading && allSearchResultsByType[type].data?.search?.entities.length === 0
);
});
const noResultsView = <List style={{ margin: '28px 160px' }} bordered dataSource={[]} />;
return (
<>
{loading && <Message type="loading" content="Loading..." style={{ marginTop: '10%' }} />}
{noResults && noResultsView}
{Object.keys(allSearchResultsByType).map((type: any) => {
const entities = allSearchResultsByType[type].data?.search?.entities;
if (entities && entities.length > 0) {
return <EntityGroupSearchResults type={type} query={query} entities={entities} />;
}
return null;
})}
</>
);
};

View File

@ -2,7 +2,6 @@ import { ArrowRightOutlined } from '@ant-design/icons';
import { Button, Card, Divider, List, Space, Typography } from 'antd';
import * as React from 'react';
import { useHistory } from 'react-router-dom';
import { useGetSearchResultsQuery } from '../../graphql/search.generated';
import { EntityType } from '../../types.generated';
import { IconStyleType } from '../entity/Entity';
import { useEntityRegistry } from '../useEntityRegistry';
@ -12,7 +11,7 @@ const styles = {
header: { marginBottom: 20 },
resultHeaderCardBody: { padding: '16px 24px' },
resultHeaderCard: { right: '52px', top: '-40px', position: 'absolute' },
resultList: { width: '100%', borderColor: '#f0f0f0', marginTop: '12px', padding: '16px 32px' },
resultList: { width: '100%', borderColor: '#f0f0f0', marginTop: '8px', padding: '16px 48px' },
seeAllButton: { fontSize: 18 },
resultsContainer: { width: '100%', padding: '40px 132px' },
};
@ -20,45 +19,26 @@ const styles = {
interface Props {
type: EntityType;
query: string;
entities: Array<any>;
}
const RESULTS_PER_GROUP = 3;
export const EntityGroupSearchResults = ({ type, query }: Props) => {
export const EntityGroupSearchResults = ({ type, query, entities }: Props) => {
const history = useHistory();
const entityRegistry = useEntityRegistry();
const { data } = useGetSearchResultsQuery({
variables: {
input: {
type,
query,
start: 0,
count: RESULTS_PER_GROUP,
filters: null,
},
},
});
if (!data?.search?.entities.length) {
return null;
}
const results = data?.search?.entities || [];
return (
<Space direction="vertical" style={styles.resultsContainer}>
<List
header={
<span style={styles.header}>
<Typography.Title level={3}>{entityRegistry.getCollectionName(type)}</Typography.Title>
<Typography.Title level={2}>{entityRegistry.getCollectionName(type)}</Typography.Title>
<Card bodyStyle={styles.resultHeaderCardBody} style={styles.resultHeaderCard as any}>
{entityRegistry.getIcon(type, 36, IconStyleType.ACCENT)}
</Card>
</span>
}
footer={
data?.search &&
data?.search?.total > 0 && (
entities.length > 0 && (
<Button
type="text"
style={styles.seeAllButton}
@ -80,12 +60,12 @@ export const EntityGroupSearchResults = ({ type, query }: Props) => {
)
}
style={styles.resultList}
dataSource={results}
dataSource={entities}
split={false}
renderItem={(item, index) => (
<>
<List.Item>{entityRegistry.renderSearchResult(type, item)}</List.Item>
{index < results.length - 1 && <Divider />}
{index < entities.length - 1 && <Divider />}
</>
)}
bordered

View File

@ -11,7 +11,7 @@ import useFilters from './utils/useFilters';
import { navigateToSearchUrl } from './utils/navigateToSearchUrl';
import { EntitySearchResults } from './EntitySearchResults';
import { IconStyleType } from '../entity/Entity';
import { EntityGroupSearchResults } from './EntityGroupSearchResults';
import { AllEntitiesSearchResults } from './AllEntitiesSearchResults';
const ALL_ENTITIES_TAB_NAME = 'All';
@ -106,9 +106,7 @@ export const SearchPage = () => {
onChangePage={onChangePage}
/>
) : (
entityRegistry
.getSearchEntityTypes()
.map((entityType) => <EntityGroupSearchResults type={entityType} query={query} />)
<AllEntitiesSearchResults query={query} />
)}
</SearchablePage>
);

View File

@ -1,46 +1,28 @@
import { EntityType, SearchInput } from '../../types.generated';
import { useGetSearchResultsQuery } from '../../graphql/search.generated';
import { useEntityRegistry } from '../../app/useEntityRegistry';
type AllEntityInput<T, K> = Pick<T, Exclude<keyof T, keyof K>> & K;
export function useGetAllEntitySearchResults(input: AllEntityInput<SearchInput, { type?: EntityType }>) {
const result: any = {};
result[EntityType.Chart] = useGetSearchResultsQuery({
variables: {
input: {
type: EntityType.Chart,
...input,
},
},
});
const entityRegistry = useEntityRegistry();
result[EntityType.Dashboard] = useGetSearchResultsQuery({
variables: {
input: {
type: EntityType.Dashboard,
...input,
},
},
});
const searchTypes = entityRegistry.getSearchEntityTypes();
result[EntityType.DataPlatform] = useGetSearchResultsQuery({
variables: {
input: {
type: EntityType.DataPlatform,
...input,
for (let i = 0; i < searchTypes.length; i++) {
const type = searchTypes[i];
// eslint-disable-next-line react-hooks/rules-of-hooks
result[type] = useGetSearchResultsQuery({
variables: {
input: {
type,
...input,
},
},
},
});
result[EntityType.Dataset] = useGetSearchResultsQuery({
variables: {
input: {
type: EntityType.Dataset,
...input,
},
},
});
});
}
return result;
}