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(propagation): UI for rendering propagated column documentation #11047

Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
55a94e7
UI for Documentation Propagation
samblackk Jul 30, 2024
fc325d2
Cypress prettier format fix
samblackk Jul 30, 2024
53f462b
Adding some fluff
Jul 31, 2024
ca9b85a
adding propagation action
Jul 31, 2024
08bcea4
Merge remote-tracking branch 'acryl/jj--propagation-doc-action-ui-tak…
Jul 31, 2024
2cc7081
UI for Documentation Propagation
samblackk Jul 30, 2024
15c2580
Cypress prettier format fix
samblackk Jul 30, 2024
23eba77
Adding some fluff
Jul 31, 2024
907cba4
adding propagation action
Jul 31, 2024
486f7bb
Adding
Jul 31, 2024
2773e04
Merge remote-tracking branch 'acryl/jj--propagation-doc-action-ui-tak…
Jul 31, 2024
3b3ef05
Update docPropagation.js
jjoyce0510 Jul 31, 2024
0598921
Update DescriptionModal.tsx
jjoyce0510 Jul 31, 2024
1c86fdd
Adding propagation details fixes
Jul 31, 2024
c1da3d7
Merge remote-tracking branch 'acryl/jj--propagation-doc-action-ui-tak…
Jul 31, 2024
215a31a
pushing lint fix
Aug 9, 2024
bf3b739
Adding lint fixes
Aug 9, 2024
d1487bc
Final docs and refactor
Aug 10, 2024
54bdf6b
Fint lint
Aug 10, 2024
e32eef2
Merge remote-tracking branch 'acryl/jj--propagation-doc-action-ui-tak…
Aug 10, 2024
86db1bc
Merge remote-tracking branch 'acryl/jj--propagation-doc-action-ui-tak…
Aug 10, 2024
298e9e4
Fixing lint
Aug 10, 2024
c4a313b
Merge remote-tracking branch 'acryl/jj--propagation-doc-action-ui-tak…
Aug 10, 2024
edbc0db
Adding fixes
Aug 20, 2024
b0c61a1
Merge remote-tracking branch 'acryl/jj--propagation-doc-action-ui-tak…
Aug 20, 2024
5945235
Merge branch 'master' into jj--propagation-doc-action-ui-takeover
jjoyce0510 Aug 20, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import styled from 'styled-components';
import { FetchResult } from '@apollo/client';

import { UpdateDatasetMutation } from '../../../../../../graphql/dataset.generated';
import { StringMapEntry } from '../../../../../../types.generated';
import PropagationDetails from '../../../../shared/propagation/PropagationDetails';
import UpdateDescriptionModal from '../../../../shared/components/legacy/DescriptionModal';
import StripMarkdownText, { removeMarkdown } from '../../../../shared/components/styled/StripMarkdownText';
import SchemaEditableContext from '../../../../../shared/SchemaEditableContext';
Expand All @@ -28,6 +30,11 @@ const ExpandedActions = styled.div`
height: 10px;
`;

const DescriptionWrapper = styled.span`
display: inline-flex;
align-items: center;
`;

const DescriptionContainer = styled.div`
position: relative;
display: flex;
Expand Down Expand Up @@ -105,6 +112,8 @@ type Props = {
isEdited?: boolean;
isReadOnly?: boolean;
businessAttributeDescription?: string;
isPropagated?: boolean;
sourceDetail?: StringMapEntry[] | null;
};

const ABBREVIATED_LIMIT = 80;
Expand All @@ -120,6 +129,8 @@ export default function DescriptionField({
original,
isReadOnly,
businessAttributeDescription,
isPropagated,
sourceDetail,
}: Props) {
const [showAddModal, setShowAddModal] = useState(false);
const overLimit = removeMarkdown(description).length > 80;
Expand Down Expand Up @@ -163,7 +174,7 @@ export default function DescriptionField({

return (
<DescriptionContainer>
{expanded || !overLimit ? (
{expanded ? (
<>
{!!description && <StyledViewer content={description} readOnly />}
{!!description && (EditButton || overLimit) && (
Expand All @@ -184,25 +195,29 @@ export default function DescriptionField({
</>
) : (
<>
<StripMarkdownText
limit={ABBREVIATED_LIMIT}
readMore={
<>
<Typography.Link
onClick={(e) => {
e.stopPropagation();
handleExpanded(true);
}}
>
Read More
</Typography.Link>
</>
}
suffix={EditButton}
shouldWrap
>
{description}
</StripMarkdownText>
<DescriptionWrapper>
{isPropagated && <PropagationDetails sourceDetail={sourceDetail} />}
&nbsp;
<StripMarkdownText
limit={ABBREVIATED_LIMIT}
readMore={
<>
<Typography.Link
onClick={(e) => {
e.stopPropagation();
handleExpanded(true);
}}
>
Read More
</Typography.Link>
</>
}
suffix={EditButton}
shouldWrap
>
{description}
</StripMarkdownText>
</DescriptionWrapper>
</>
)}
{isEdited && <EditedLabel>(edited)</EditedLabel>}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,29 @@ const StyledViewer = styled(Editor)`
}
`;

const OriginalDocumentation = styled(Form.Item)`
margin-bottom: 0;
`;

type Props = {
title: string;
description?: string | undefined;
original?: string | undefined;
propagatedDescription?: string | undefined;
onClose: () => void;
onSubmit: (description: string) => void;
isAddDesc?: boolean;
};

export default function UpdateDescriptionModal({ title, description, original, onClose, onSubmit, isAddDesc }: Props) {
export default function UpdateDescriptionModal({
title,
description,
original,
propagatedDescription,
onClose,
onSubmit,
isAddDesc,
}: Props) {
const [updatedDesc, setDesc] = useState(description || original || '');

const handleEditorKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
Expand Down Expand Up @@ -64,17 +77,17 @@ export default function UpdateDescriptionModal({ title, description, original, o
>
<Form layout="vertical">
<Form.Item>
<StyledEditor
content={updatedDesc}
onChange={setDesc}
dataTestId="description-editor"
onKeyDown={handleEditorKeyDown}
/>
<StyledEditor content={updatedDesc} onChange={setDesc} onKeyDown={handleEditorKeyDown} />
</Form.Item>
{!isAddDesc && description && original && (
<Form.Item label={<FormLabel>Original:</FormLabel>}>
<OriginalDocumentation label={<FormLabel>Original:</FormLabel>}>
<StyledViewer content={original || ''} readOnly />
</Form.Item>
</OriginalDocumentation>
)}
{!isAddDesc && description && propagatedDescription && (
<OriginalDocumentation label={<FormLabel>Propagated:</FormLabel>}>
<StyledViewer content={propagatedDescription || ''} readOnly />
</OriginalDocumentation>
)}
</Form>
</Modal>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from 'react';
import styled from 'styled-components';
import { Popover } from 'antd';
import { StringMapEntry } from '../../../../types.generated';
import PropagationEntityLink from './PropagationEntityLink';
import { usePropagationDetails } from './utils';
import { ANTD_GRAY } from '../constants';
import { PropagateThunderbolt, PropagateThunderboltFilled } from './PropagationIcon';

const PopoverWrapper = styled.div`
display: flex;
flex-direction: column;
`;

const PopoverTitle = styled.div`
font-weight: bold;
font-size: 14px;
padding: 6px 0px;
`;

const PopoverDescription = styled.div`
max-width: 340px;
font-size: 14px;
color: ${ANTD_GRAY[7]};
display: inline;
padding: 0px 0px 4px 0px;
`;

interface Props {
sourceDetail?: StringMapEntry[] | null;
}

export default function PropagationDetails({ sourceDetail }: Props) {
const {
isPropagated,
origin: { entity: originEntity },
via: { entity: viaEntity },
} = usePropagationDetails(sourceDetail);

if (!sourceDetail || !isPropagated) return null;

const popoverContent =
originEntity || viaEntity ? (
<PopoverWrapper>
<PopoverDescription>
This description was automatically propagated{' '}
{originEntity && originEntity.urn !== viaEntity?.urn && (
<>
from <PropagationEntityLink entity={originEntity} />
</>
)}
{viaEntity && (
<>
via <PropagationEntityLink entity={viaEntity} />
</>
)}
</PopoverDescription>
</PopoverWrapper>
) : undefined;

return (
<Popover
showArrow={false}
title={
<PopoverTitle>
<PropagateThunderboltFilled />
Propagated Description
</PopoverTitle>
}
content={popoverContent}
>
<PropagateThunderbolt data-testid="docPropagationIndicator" />
</Popover>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import styled from 'styled-components';
import { Link } from 'react-router-dom';
import { useEntityRegistry } from '../../../useEntityRegistry';
import { Entity, EntityType, SchemaFieldEntity } from '../../../../types.generated';
import { GenericEntityProperties } from '../types';

const PreviewImage = styled.img<{ size: number }>`
height: ${(props) => props.size}px;
width: ${(props) => props.size}px;
min-width: ${(props) => props.size}px;
object-fit: contain;
background-color: transparent;
margin: 0 3px;
`;

const StyledLink = styled(Link)`
display: flex;
align-items: center;
`;

interface Props {
entity: Entity;
}

export default function PropagationEntityLink({ entity }: Props) {
const entityRegistry = useEntityRegistry();

const isSchemaField = entity.type === EntityType.SchemaField;
const baseEntity = isSchemaField ? (entity as SchemaFieldEntity).parent : entity;

Comment on lines +33 to +35
Copy link
Contributor

Choose a reason for hiding this comment

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

Optimize the entity type check and assignment.

The entity type check and assignment can be optimized for better readability and performance.

-  const isSchemaField = entity.type === EntityType.SchemaField;
-  const baseEntity = isSchemaField ? (entity as SchemaFieldEntity).parent : entity;
+  const baseEntity = entity.type === EntityType.SchemaField ? (entity as SchemaFieldEntity).parent : entity;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const isSchemaField = entity.type === EntityType.SchemaField;
const baseEntity = isSchemaField ? (entity as SchemaFieldEntity).parent : entity;
const baseEntity = entity.type === EntityType.SchemaField ? (entity as SchemaFieldEntity).parent : entity;

const logoUrl = (baseEntity as GenericEntityProperties)?.platform?.properties?.logoUrl || '';
let entityUrl = entityRegistry.getEntityUrl(baseEntity.type, baseEntity.urn);
let entityDisplayName = entityRegistry.getDisplayName(baseEntity.type, baseEntity);

if (isSchemaField) {
entityUrl = `${entityUrl}/${encodeURIComponent('Columns')}?schemaFilter=${encodeURIComponent(
(entity as SchemaFieldEntity).fieldPath,
)}`;
const schemaFieldName = entityRegistry.getDisplayName(entity.type, entity);
entityDisplayName = `${entityDisplayName}.${schemaFieldName}`;
}

return (
<StyledLink to={entityUrl}>
<PreviewImage src={logoUrl} alt="test" size={14} />
{entityDisplayName}
</StyledLink>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import styled from 'styled-components';
import { ThunderboltFilled } from '@ant-design/icons';
import { REDESIGN_COLORS } from '../constants';

export const PropagateThunderbolt = styled(ThunderboltFilled)`
&& {
color: #a7c7fa;
}
font-size: 16px;
&:hover {
color: ${REDESIGN_COLORS.BLUE};
}
margin-right: 4px;
`;

export const PropagateThunderboltFilled = styled(ThunderboltFilled)`
&& {
color: ${REDESIGN_COLORS.BLUE};
}
font-size: 16px;
margin-right: 4px;
`;
24 changes: 24 additions & 0 deletions datahub-web-react/src/app/entity/shared/propagation/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { StringMapEntry } from '../../../../types.generated';
import { useGetEntities } from '../useGetEntities';

export function usePropagationDetails(sourceDetail?: StringMapEntry[] | null) {
const isPropagated = !!sourceDetail?.find((mapEntry) => mapEntry.key === 'propagated' && mapEntry.value === 'true');
const originEntityUrn = sourceDetail?.find((mapEntry) => mapEntry.key === 'origin')?.value || '';
const viaEntityUrn = sourceDetail?.find((mapEntry) => mapEntry.key === 'via')?.value || '';
Comment on lines +4 to +7
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider handling edge cases for sourceDetail.

Ensure that sourceDetail is properly validated to avoid potential issues with find operations.

-  const isPropagated = !!sourceDetail?.find((mapEntry) => mapEntry.key === 'propagated' && mapEntry.value === 'true');
-  const originEntityUrn = sourceDetail?.find((mapEntry) => mapEntry.key === 'origin')?.value || '';
-  const viaEntityUrn = sourceDetail?.find((mapEntry) => mapEntry.key === 'via')?.value || '';
+  const isPropagated = sourceDetail?.some((mapEntry) => mapEntry.key === 'propagated' && mapEntry.value === 'true') ?? false;
+  const originEntityUrn = sourceDetail?.find((mapEntry) => mapEntry.key === 'origin')?.value ?? '';
+  const viaEntityUrn = sourceDetail?.find((mapEntry) => mapEntry.key === 'via')?.value ?? '';
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function usePropagationDetails(sourceDetail?: StringMapEntry[] | null) {
const isPropagated = !!sourceDetail?.find((mapEntry) => mapEntry.key === 'propagated' && mapEntry.value === 'true');
const originEntityUrn = sourceDetail?.find((mapEntry) => mapEntry.key === 'origin')?.value || '';
const viaEntityUrn = sourceDetail?.find((mapEntry) => mapEntry.key === 'via')?.value || '';
export function usePropagationDetails(sourceDetail?: StringMapEntry[] | null) {
const isPropagated = sourceDetail?.some((mapEntry) => mapEntry.key === 'propagated' && mapEntry.value === 'true') ?? false;
const originEntityUrn = sourceDetail?.find((mapEntry) => mapEntry.key === 'origin')?.value ?? '';
const viaEntityUrn = sourceDetail?.find((mapEntry) => mapEntry.key === 'via')?.value ?? '';


const entities = useGetEntities([originEntityUrn, viaEntityUrn]);
const originEntity = entities.find((e) => e.urn === originEntityUrn);
const viaEntity = entities.find((e) => e.urn === viaEntityUrn);

return {
isPropagated,
origin: {
urn: originEntityUrn,
entity: originEntity,
},
via: {
urn: viaEntityUrn,
entity: viaEntity,
},
};
}
Loading
Loading