Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(schema) Add search filter to Schema tab #5845

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions datahub-web-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"react": "^17.0.0",
"react-color": "^2.19.3",
"react-dom": "^17.0.0",
"react-highlighter": "^0.4.3",
"react-icons": "4.3.1",
"react-js-cron": "^2.1.0",
"react-router": "^5.2.0",
Expand Down
4 changes: 2 additions & 2 deletions datahub-web-react/src/Mocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,7 @@ export const container2 = {
__typename: 'Container',
} as Container;

const glossaryTerm1 = {
export const glossaryTerm1 = {
urn: 'urn:li:glossaryTerm:1',
type: EntityType.GlossaryTerm,
name: 'Another glossary term',
Expand Down Expand Up @@ -1077,7 +1077,7 @@ export const glossaryNode5 = {
__typename: 'GlossaryNode',
} as GlossaryNode;

const sampleTag = {
export const sampleTag = {
urn: 'urn:li:tag:abc-sample-tag',
name: 'abc-sample-tag',
description: 'sample tag description',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
import React from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import { Button, Popover, Select, Tooltip, Typography } from 'antd';
import { Button, Input, Popover, Select, Tooltip, Typography } from 'antd';
import { debounce } from 'lodash';
import {
AuditOutlined,
CaretDownOutlined,
FileTextOutlined,
QuestionCircleOutlined,
SearchOutlined,
TableOutlined,
} from '@ant-design/icons';
import styled from 'styled-components';
import styled from 'styled-components/macro';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whats up with this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is what I mentioned to you some time back where if you import from /macro and look in the browser dev tools and check out the html, styled component names are shown, making it much easier to find what styled component is what

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aahhh right! gotta start doing this myself

import CustomPagination from './CustomPagination';
import TabToolbar from '../../../../shared/components/styled/TabToolbar';
import { SemanticVersionStruct } from '../../../../../../types.generated';
import { toRelativeTimeString } from '../../../../../shared/time/timeUtils';
import { ANTD_GRAY, REDESIGN_COLORS } from '../../../../shared/constants';
import { navigateToVersionedDatasetUrl } from '../../../../shared/tabs/Dataset/Schema/utils/navigateToVersionedDatasetUrl';
import SchemaTimeStamps from './SchemaTimeStamps';
import getSchemaFilterFromQueryString from '../../../../shared/tabs/Dataset/Schema/utils/getSchemaFilterFromQueryString';

const SchemaHeaderContainer = styled.div`
display: flex;
justify-content: space-between;
padding-bottom: 16px;
width: 100%;
`;

Expand Down Expand Up @@ -60,11 +62,12 @@ const ValueButton = styled(Button)<{ $highlighted: boolean }>`

const KeyValueButtonGroup = styled.div`
margin-right: 10px;
display: inline-block;
display: flex;
`;

// Below styles are for buttons on the right side of the Schema Header
const RightButtonsGroup = styled.div`
padding-left: 5px;
&&& {
display: flex;
justify-content: right;
Expand Down Expand Up @@ -111,6 +114,14 @@ const StyledCaretDownOutlined = styled(CaretDownOutlined)`
}
`;

const StyledInput = styled(Input)`
border-radius: 70px;
max-width: 300px;
`;

const MAX_ROWS_BEFORE_DEBOUNCE = 50;
const HALF_SECOND_IN_MS = 500;

type Props = {
maxVersion?: number;
fetchVersions?: (version1: number, version2: number) => void;
Expand All @@ -128,6 +139,8 @@ type Props = {
versionList: Array<SemanticVersionStruct>;
showSchemaAuditView: boolean;
setShowSchemaAuditView: any;
setFilterText: (text: string) => void;
numRows: number;
};

export default function SchemaHeader({
Expand All @@ -147,6 +160,8 @@ export default function SchemaHeader({
versionList,
showSchemaAuditView,
setShowSchemaAuditView,
setFilterText,
numRows,
}: Props) {
const history = useHistory();
const location = useLocation();
Expand Down Expand Up @@ -182,6 +197,12 @@ export default function SchemaHeader({
};
const schemaAuditToggleText = showSchemaAuditView ? 'Close column history' : 'View column history';

const debouncedSetFilterText = debounce(
(e: React.ChangeEvent<HTMLInputElement>) => setFilterText(e.target.value),
numRows > MAX_ROWS_BEFORE_DEBOUNCE ? HALF_SECOND_IN_MS : 0,
);
const schemaFilter = getSchemaFilterFromQueryString(location);

const docLink = 'https://datahubproject.io/docs/dev-guides/timeline/';
return (
<TabToolbar>
Expand Down Expand Up @@ -219,6 +240,15 @@ export default function SchemaHeader({
) : (
<ShowVersionButton onClick={() => setEditMode?.(true)}>Back</ShowVersionButton>
))}
{!showRaw && (
<StyledInput
defaultValue={schemaFilter}
placeholder="Search in schema..."
onChange={debouncedSetFilterText}
allowClear
prefix={<SearchOutlined />}
/>
)}
</LeftButtonsGroup>
<RightButtonsGroup>
<SchemaTimeStamps lastObserved={lastObserved} lastUpdated={lastUpdated} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { Typography } from 'antd';
import styled from 'styled-components';
import Highlight from 'react-highlighter';
import translateFieldPath from './translateFieldPath';
import { ExtendedSchemaFields } from './types';
import TypeLabel from '../../../../shared/tabs/Dataset/Schema/components/TypeLabel';
Expand Down Expand Up @@ -32,6 +33,7 @@ const FieldPathText = styled(Typography.Text)`
export default function useSchemaTitleRenderer(
schemaMetadata: SchemaMetadata | undefined | null,
setSelectedFkFieldPath: (params: { fieldPath: string; constraint?: ForeignKeyConstraint | null } | null) => void,
filterText: string,
) {
const [highlightedConstraint, setHighlightedConstraint] = useState<string | null>(null);

Expand All @@ -54,7 +56,9 @@ export default function useSchemaTitleRenderer(
return (
<>
<FieldPathContainer>
<FieldPathText>{pathToDisplay}</FieldPathText>
<FieldPathText>
<Highlight search={filterText}>{pathToDisplay}</Highlight>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fancy! bummer that it doesn't work on nested components though :/ i think we'll need to build that for our search cards otherwise we'll need to render like a zillion of these.

</FieldPathText>
<TypeLabel type={record.type} nativeDataType={record.nativeDataType} />
{(schemaMetadata?.primaryKeys?.includes(fieldPath) || record.isPartOfKey) && <PrimaryKeyLabel />}
{schemaMetadata?.foreignKeys
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Empty } from 'antd';
import React, { useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import { useLocation } from 'react-router';
import { GetDatasetQuery } from '../../../../../../graphql/dataset.generated';
import { useGetSchemaBlameQuery, useGetSchemaVersionListQuery } from '../../../../../../graphql/schemaBlame.generated';
import SchemaEditableContext from '../../../../../shared/SchemaEditableContext';
Expand All @@ -14,6 +15,10 @@ import { SchemaFieldBlame, SemanticVersionStruct } from '../../../../../../types
import SchemaTable from './SchemaTable';
import useGetSemanticVersionFromUrlParams from './utils/useGetSemanticVersionFromUrlParams';
import { useGetVersionedDatasetQuery } from '../../../../../../graphql/versionedDataset.generated';
import { useEntityRegistry } from '../../../../../useEntityRegistry';
import { filterSchemaRows } from './utils/filterSchemaRows';
import getSchemaFilterFromQueryString from './utils/getSchemaFilterFromQueryString';
import useUpdateSchemaFilterQueryString from './utils/updateSchemaFilterQueryString';

const NoSchema = styled(Empty)`
color: ${ANTD_GRAY[6]};
Expand All @@ -26,13 +31,19 @@ const SchemaTableContainer = styled.div`
`;
export const SchemaTab = ({ properties }: { properties?: any }) => {
const { entityData } = useEntityData();
const entityRegistry = useEntityRegistry();
const baseEntity = useBaseEntity<GetDatasetQuery>();
const maybeEntityData = entityData || {};
let schemaMetadata: any = maybeEntityData?.schemaMetadata || undefined;
let editableSchemaMetadata: any = maybeEntityData?.editableSchemaMetadata || undefined;
const datasetUrn: string = baseEntity?.dataset?.urn || '';
const usageStats = baseEntity?.dataset?.usageStats;
const [showRaw, setShowRaw] = useState(false);
const location = useLocation();
const schemaFilter = getSchemaFilterFromQueryString(location);
const [filterText, setFilterText] = useState(schemaFilter);
useUpdateSchemaFilterQueryString(filterText);

const hasRawSchema = useMemo(
() =>
schemaMetadata?.platformSchema?.__typename === 'TableSchema' &&
Expand Down Expand Up @@ -113,9 +124,17 @@ export const SchemaTab = ({ properties }: { properties?: any }) => {
setShowKeySchema(true);
}
}, [hasValueSchema, hasKeySchema, setShowKeySchema]);

const { filteredRows, expandedRowsFromFilter } = filterSchemaRows(
schemaMetadata?.fields,
editableSchemaMetadata,
filterText,
entityRegistry,
);

const rows = useMemo(() => {
return groupByFieldPath(schemaMetadata?.fields, { showKeySchema });
}, [schemaMetadata, showKeySchema]);
return groupByFieldPath(filteredRows, { showKeySchema });
}, [showKeySchema, filteredRows]);

const lastUpdated = getSchemaBlameData?.getSchemaBlame?.version?.semanticVersionTimestamp;
const lastObserved = versionedDatasetData.data?.versionedDataset?.schema?.lastObserved;
Expand All @@ -139,6 +158,8 @@ export const SchemaTab = ({ properties }: { properties?: any }) => {
versionList={versionList}
showSchemaAuditView={showSchemaAuditView}
setShowSchemaAuditView={setShowSchemaAuditView}
setFilterText={setFilterText}
numRows={rows.length}
/>
<SchemaTableContainer>
{/* eslint-disable-next-line no-nested-ternary */}
Expand All @@ -159,6 +180,8 @@ export const SchemaTab = ({ properties }: { properties?: any }) => {
usageStats={usageStats}
schemaFieldBlameList={schemaFieldBlameList}
showSchemaAuditView={showSchemaAuditView}
expandedRowsFromFilter={expandedRowsFromFilter as any}
filterText={filterText as any}
/>
</SchemaEditableContext.Provider>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { ColumnsType } from 'antd/es/table';
import styled from 'styled-components';
import {} from 'antd';
Expand Down Expand Up @@ -45,6 +45,8 @@ export type Props = {
usageStats?: UsageQueryResult | null;
schemaFieldBlameList?: Array<SchemaFieldBlame> | null;
showSchemaAuditView: boolean;
expandedRowsFromFilter?: Set<string>;
filterText?: string;
};
export default function SchemaTable({
rows,
Expand All @@ -54,6 +56,8 @@ export default function SchemaTable({
editMode = true,
schemaFieldBlameList,
showSchemaAuditView,
expandedRowsFromFilter = new Set(),
filterText = '',
}: Props): JSX.Element {
const hasUsageStats = useMemo(() => (usageStats?.aggregations?.fields?.length || 0) > 0, [usageStats]);

Expand All @@ -63,15 +67,27 @@ export default function SchemaTable({

const descriptionRender = useDescriptionRenderer(editableSchemaMetadata);
const usageStatsRenderer = useUsageStatsRenderer(usageStats);
const tagRenderer = useTagsAndTermsRenderer(editableSchemaMetadata, tagHoveredIndex, setTagHoveredIndex, {
showTags: true,
showTerms: false,
});
const termRenderer = useTagsAndTermsRenderer(editableSchemaMetadata, tagHoveredIndex, setTagHoveredIndex, {
showTags: false,
showTerms: true,
});
const schemaTitleRenderer = useSchemaTitleRenderer(schemaMetadata, setSelectedFkFieldPath);
const tagRenderer = useTagsAndTermsRenderer(
editableSchemaMetadata,
tagHoveredIndex,
setTagHoveredIndex,
{
showTags: true,
showTerms: false,
},
filterText,
);
const termRenderer = useTagsAndTermsRenderer(
editableSchemaMetadata,
tagHoveredIndex,
setTagHoveredIndex,
{
showTags: false,
showTerms: true,
},
filterText,
);
const schemaTitleRenderer = useSchemaTitleRenderer(schemaMetadata, setSelectedFkFieldPath, filterText);
const schemaBlameRenderer = useSchemaBlameRenderer(schemaFieldBlameList);

const onTagTermCell = (record: SchemaField, rowIndex: number | undefined) => ({
Expand Down Expand Up @@ -153,6 +169,17 @@ export default function SchemaTable({
allColumns = [...allColumns, blameColumn];
}

const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set());

useEffect(() => {
setExpandedRows((previousRows) => {
const finalRowsSet = new Set();
expandedRowsFromFilter.forEach((row) => finalRowsSet.add(row));
previousRows.forEach((row) => finalRowsSet.add(row));
return finalRowsSet as Set<string>;
});
}, [expandedRowsFromFilter]);

return (
<FkContext.Provider value={selectedFkFieldPath}>
<TableContainer>
Expand All @@ -169,9 +196,20 @@ export default function SchemaTable({
},
}}
expandable={{
expandedRowKeys: [...Array.from(expandedRows)],
defaultExpandAllRows: false,
expandRowByClick: false,
expandIcon: ExpandIcon,
onExpand: (expanded, record) => {
if (expanded) {
setExpandedRows((previousRows) => new Set(previousRows.add(record.fieldPath)));
} else {
setExpandedRows((previousRows) => {
previousRows.delete(record.fieldPath);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tricky!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ugh i know - updating Sets in state isn't as simple as you'd want it to be

return new Set(previousRows);
});
}
},
indentSize: 0,
}}
pagination={false}
Expand Down
Loading