Skip to content

Commit

Permalink
feat(ui): Add caching to search, entity profile for better UX (datahu…
Browse files Browse the repository at this point in the history
  • Loading branch information
jjoyce0510 authored Dec 1, 2023
1 parent 7b0a8f4 commit f3abfd1
Show file tree
Hide file tree
Showing 14 changed files with 241 additions and 119 deletions.
149 changes: 125 additions & 24 deletions datahub-web-react/src/Mocks.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ export const EmbeddedListSearch = ({
variables: {
input: searchInput,
},
fetchPolicy: 'cache-first',
});

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import React from 'react';
import { Pagination, Typography } from 'antd';
import { LoadingOutlined } from '@ant-design/icons';
import styled from 'styled-components';
import { FacetFilterInput, FacetMetadata, SearchResults as SearchResultType } from '../../../../../../types.generated';
import { SearchCfg } from '../../../../../../conf';
import { ReactComponent as LoadingSvg } from '../../../../../../images/datahub-logo-color-loading_pendulum.svg';
import { EntityAndType } from '../../../types';
import { UnionType } from '../../../../../search/utils/constants';
import { SearchFiltersSection } from '../../../../../search/SearchFiltersSection';
import { EntitySearchResults, EntityActionProps } from './EntitySearchResults';
import MatchingViewsLabel from './MatchingViewsLabel';
import { ANTD_GRAY } from '../../../constants';

const SearchBody = styled.div`
height: 100%;
Expand Down Expand Up @@ -59,6 +60,12 @@ const LoadingContainer = styled.div`
flex: 1;
`;

const StyledLoading = styled(LoadingOutlined)`
font-size: 36px;
color: ${ANTD_GRAY[7]};
padding-bottom: 18px;
]`;

interface Props {
page: number;
searchResponse?: SearchResultType | null;
Expand Down Expand Up @@ -121,7 +128,7 @@ export const EmbeddedListSearchResults = ({
<ResultContainer>
{loading && (
<LoadingContainer>
<LoadingSvg height={80} width={80} />
<StyledLoading />
</LoadingContainer>
)}
{!loading && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const EntityTabs = <T,>({ tabs, selectedTab }: Props) => {

return (
<UnborderedTabs
animated={false}
activeKey={selectedTab?.name || ''}
size="large"
onTabClick={(tab: string) => routeToTab({ tabName: tab })}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default function useGetDataForProfile<T>({ urn, entityType, useEntityQuer
refetch,
} = useEntityQuery({
variables: { urn },
fetchPolicy: 'cache-first',
});

const dataPossiblyCombinedWithSiblings = isHideSiblingMode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default function generateUseSearchResultsViaRelationshipHook({
variables: {
input: inputFields,
},
fetchPolicy: 'cache-first',
skip: !filtersExist(filters, orFilters), // If you don't include any filters, we shound't return anything :). Might as well skip!
});

Expand Down
2 changes: 2 additions & 0 deletions datahub-web-react/src/app/search/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export const SearchPage = () => {
searchFlags: { getSuggestions: true },
},
},
fetchPolicy: 'cache-and-network',
});

const total = data?.searchAcrossEntities?.total || 0;
Expand Down Expand Up @@ -217,6 +218,7 @@ export const SearchPage = () => {
)}
{showSearchFiltersV2 && (
<SearchFilters
loading={loading}
availableFilters={data?.searchAcrossEntities?.facets || []}
activeFilters={filters}
unionType={unionType}
Expand Down
4 changes: 3 additions & 1 deletion datahub-web-react/src/app/search/SearchResultList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const ListItem = styled.div<{ isSelectMode: boolean }>`
`;

type Props = {
loading: boolean;
query: string;
searchResults: CombinedSearchResult[];
totalResultCount: number;
Expand All @@ -64,6 +65,7 @@ type Props = {
};

export const SearchResultList = ({
loading,
query,
searchResults,
totalResultCount,
Expand Down Expand Up @@ -104,7 +106,7 @@ export const SearchResultList = ({
id="search-result-list"
dataSource={searchResults}
split={false}
locale={{ emptyText: <EmptySearchResults suggestions={suggestions} /> }}
locale={{ emptyText: (!loading && <EmptySearchResults suggestions={suggestions} />) || <></> }}
renderItem={(item, index) => (
<ResultWrapper showUpdatedStyles={showSearchFiltersV2} className={`entityUrn-${item.entity.urn}`}>
<ListItem
Expand Down
11 changes: 6 additions & 5 deletions datahub-web-react/src/app/search/SearchResults.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import { Pagination, Typography } from 'antd';
import styled from 'styled-components/macro';
import { Message } from '../shared/Message';
import { Entity, FacetFilterInput, FacetMetadata, MatchedField, SearchSuggestion } from '../../types.generated';
import { SearchCfg } from '../../conf';
import { SearchResultsRecommendations } from './SearchResultsRecommendations';
Expand Down Expand Up @@ -29,6 +28,7 @@ import { combineSiblingsInSearchResults } from './utils/combineSiblingsInSearchR
import SearchQuerySuggester from './suggestions/SearchQuerySugggester';
import { ANTD_GRAY_V2 } from '../entity/shared/constants';
import { formatNumberWithoutAbbreviation } from '../shared/formatNumber';
import SearchResultsLoadingSection from './SearchResultsLoadingSection';

const SearchResultsWrapper = styled.div<{ v2Styles: boolean }>`
display: flex;
Expand Down Expand Up @@ -109,6 +109,7 @@ const SearchResultListContainer = styled.div<{ v2Styles: boolean }>`
`;

interface Props {
loading: boolean;
unionType?: UnionType;
query: string;
viewUrn?: string;
Expand All @@ -124,7 +125,6 @@ interface Props {
} | null;
facets?: Array<FacetMetadata> | null;
selectedFilters: Array<FacetFilterInput>;
loading: boolean;
error: any;
onChangeFilters: (filters: Array<FacetFilterInput>) => void;
onChangeUnionType: (unionType: UnionType) => void;
Expand All @@ -142,14 +142,14 @@ interface Props {
}

export const SearchResults = ({
loading,
unionType = UnionType.AND,
query,
viewUrn,
page,
searchResponse,
facets,
selectedFilters,
loading,
error,
onChangeUnionType,
onChangeFilters,
Expand Down Expand Up @@ -180,7 +180,6 @@ export const SearchResults = ({

return (
<>
{loading && <Message type="loading" content="Loading..." style={{ marginTop: '10%' }} />}
<SearchResultsWrapper v2Styles={showSearchFiltersV2}>
<SearchBody>
{!showSearchFiltersV2 && (
Expand Down Expand Up @@ -247,10 +246,12 @@ export const SearchResults = ({
</StyledTabToolbar>
)}
{(error && <ErrorSection />) ||
(!loading && (
(loading && !combinedSiblingSearchResults.length && <SearchResultsLoadingSection />) ||
(combinedSiblingSearchResults && (
<SearchResultListContainer v2Styles={showSearchFiltersV2}>
{totalResults > 0 && <SearchQuerySuggester suggestions={suggestions} />}
<SearchResultList
loading={loading}
query={query}
searchResults={combinedSiblingSearchResults}
totalResultCount={totalResults}
Expand Down
33 changes: 33 additions & 0 deletions datahub-web-react/src/app/search/SearchResultsLoadingSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as React from 'react';
import { Skeleton } from 'antd';
import styled from 'styled-components';
import { ANTD_GRAY } from '../entity/shared/constants';

const Container = styled.div`
width: 100%;
display: flex;
flex-direction: column;
padding: 24px 0px 20px 40px;
overflow: auto;
flex: 1;
`;

const cardStyle = {
backgroundColor: ANTD_GRAY[2],
height: 120,
minWidth: '98%',
borderRadius: 8,
marginBottom: 20,
};

export default function SearchResultsLoadingSection() {
return (
<Container>
<Skeleton.Input active style={cardStyle} />
<Skeleton.Input active style={cardStyle} />
<Skeleton.Input active style={cardStyle} />
<Skeleton.Input active style={cardStyle} />
<Skeleton.Input active style={cardStyle} />
</Container>
);
}
95 changes: 11 additions & 84 deletions datahub-web-react/src/app/search/__tests__/SearchPage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,23 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { render, waitFor } from '@testing-library/react';
import { InMemoryCache } from '@apollo/client';
import { MockedProvider } from '@apollo/client/testing';
import { Route } from 'react-router';

import { SearchPage } from '../SearchPage';
import TestPageContainer from '../../../utils/test-utils/TestPageContainer';
import { mocksWithSearchFlagsOff } from '../../../Mocks';
import { PageRoutes } from '../../../conf/Global';
import possibleTypesResult from '../../../possibleTypes.generated';

describe('SearchPage', () => {
it('renders loading', async () => {
const promise = Promise.resolve();
const { getByText } = render(
<MockedProvider mocks={mocksWithSearchFlagsOff} addTypename={false}>
<TestPageContainer
initialEntries={[
'/search?filter__entityType=DATASET&filter_platform=hive,kafka&page=1&query=sample',
]}
>
<Route path={PageRoutes.SEARCH_RESULTS} render={() => <SearchPage />} />
</TestPageContainer>
</MockedProvider>,
);
await waitFor(() => expect(getByText('Loading...')).toBeInTheDocument());
await act(() => promise);
});
const cache = new InMemoryCache({
// need to define possibleTypes to allow us to use Apollo cache with union types
possibleTypes: possibleTypesResult.possibleTypes,
});

describe('SearchPage', () => {
it('renders the selected filters as checked', async () => {
const { getByTestId, queryByTestId } = render(
<MockedProvider
mocks={mocksWithSearchFlagsOff}
addTypename={false}
defaultOptions={{
watchQuery: { fetchPolicy: 'no-cache' },
query: { fetchPolicy: 'no-cache' },
}}
>
<MockedProvider mocks={mocksWithSearchFlagsOff} addTypename cache={cache}>
<TestPageContainer
initialEntries={['/search?filter__entityType=DATASET&filter_platform=kafka&page=1&query=test']}
>
Expand All @@ -56,14 +37,7 @@ describe('SearchPage', () => {

it('renders the selected filters as checked using legacy URL scheme for entity (entity instead of _entityType)', async () => {
const { getByTestId, queryByTestId } = render(
<MockedProvider
mocks={mocksWithSearchFlagsOff}
addTypename={false}
defaultOptions={{
watchQuery: { fetchPolicy: 'no-cache' },
query: { fetchPolicy: 'no-cache' },
}}
>
<MockedProvider mocks={mocksWithSearchFlagsOff} addTypename cache={cache}>
<TestPageContainer
initialEntries={['/search?filter_entity=DATASET&filter_platform=kafka&page=1&query=test']}
>
Expand All @@ -83,14 +57,7 @@ describe('SearchPage', () => {

it('renders multiple checked filters at once', async () => {
const { getByTestId, queryByTestId } = render(
<MockedProvider
mocks={mocksWithSearchFlagsOff}
addTypename={false}
defaultOptions={{
watchQuery: { fetchPolicy: 'no-cache' },
query: { fetchPolicy: 'no-cache' },
}}
>
<MockedProvider mocks={mocksWithSearchFlagsOff} addTypename cache={cache}>
<TestPageContainer
initialEntries={['/search?filter__entityType=DATASET&filter_platform=kafka,hdfs&page=1&query=test']}
>
Expand All @@ -108,44 +75,4 @@ describe('SearchPage', () => {
const hdfsPlatformBox = getByTestId('facet-platform-hdfs');
expect(hdfsPlatformBox).toHaveProperty('checked', true);
});

it('clicking a filter selects a new filter', async () => {
const promise = Promise.resolve();
const { getByTestId, queryByTestId } = render(
<MockedProvider
mocks={mocksWithSearchFlagsOff}
addTypename={false}
defaultOptions={{
watchQuery: { fetchPolicy: 'no-cache' },
query: { fetchPolicy: 'no-cache' },
}}
>
<TestPageContainer
initialEntries={['/search?filter__entityType=DATASET&filter_platform=kafka&page=1&query=test']}
>
<Route path={PageRoutes.SEARCH_RESULTS} render={() => <SearchPage />} />
</TestPageContainer>
</MockedProvider>,
);

await waitFor(() => expect(queryByTestId('facet-_entityType-DATASET')).toBeInTheDocument());

const datasetEntityBox = getByTestId('facet-_entityType-DATASET');
expect(datasetEntityBox).toHaveProperty('checked', true);

const chartEntityBox = getByTestId('facet-_entityType-CHART');
expect(chartEntityBox).toHaveProperty('checked', false);
act(() => {
fireEvent.click(chartEntityBox);
});

await waitFor(() => expect(queryByTestId('facet-_entityType-DATASET')).toBeInTheDocument());

const datasetEntityBox2 = getByTestId('facet-_entityType-DATASET');
expect(datasetEntityBox2).toHaveProperty('checked', true);

const chartEntityBox2 = getByTestId('facet-_entityType-CHART');
expect(chartEntityBox2).toHaveProperty('checked', true);
await act(() => promise);
});
});
4 changes: 4 additions & 0 deletions datahub-web-react/src/app/search/filters/BasicFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from '../../onboarding/config/SearchOnboardingConfig';
import { useFilterRendererRegistry } from './render/useFilterRenderer';
import { FilterScenarioType } from './render/types';
import BasicFiltersLoadingSection from './BasicFiltersLoadingSection';

const NUM_VISIBLE_FILTER_DROPDOWNS = 5;

Expand Down Expand Up @@ -56,6 +57,7 @@ const FILTERS_TO_REMOVE = [
];

interface Props {
loading: boolean;
availableFilters: FacetMetadata[] | null;
activeFilters: FacetFilterInput[];
onChangeFilters: (newFilters: FacetFilterInput[]) => void;
Expand All @@ -64,6 +66,7 @@ interface Props {
}

export default function BasicFilters({
loading,
availableFilters,
activeFilters,
onChangeFilters,
Expand All @@ -88,6 +91,7 @@ export default function BasicFilters({
<span id={SEARCH_RESULTS_FILTERS_ID}>
<FlexSpacer>
<FlexWrapper>
{loading && !visibleFilters?.length && <BasicFiltersLoadingSection />}
{visibleFilters?.map((filter) => {
return filterRendererRegistry.hasRenderer(filter.field) ? (
filterRendererRegistry.render(filter.field, {
Expand Down
Loading

0 comments on commit f3abfd1

Please sign in to comment.