Minor: removed services || fixed header ellipsis on deleted tag (#20909)

* removed services || fixed header elicps on deleted tag

* fixed beta tags

* fixed beta tags

* removed beta url

* fixed ellipses

* fixed circular dependency

* fixed tests

* fixed quality test

* fixed dqsupport with mulitple methods

* fixed dqsupport with mulitple methods

* fixed header ellipsis

* fixed webpack url

(cherry picked from commit 3d6810f83f0e7bf563e2126d8795364f8eca8aa5)
This commit is contained in:
Dhruv Parmar 2025-04-25 11:54:05 +05:30 committed by Ashish Gupta
parent 742929315a
commit 39c724f01b
12 changed files with 173 additions and 165 deletions

View File

@ -20,7 +20,6 @@ import QueryString from 'qs';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Link, useHistory, useParams } from 'react-router-dom';
import { ReactComponent as IconExternalLink } from '../../../assets/svg/external-links.svg';
import { ReactComponent as RedAlertIcon } from '../../../assets/svg/ic-alert-red.svg';
import { ReactComponent as TaskOpenIcon } from '../../../assets/svg/ic-open-task.svg';
import { ReactComponent as VersionIcon } from '../../../assets/svg/ic-version.svg';
@ -32,7 +31,6 @@ import { OwnerLabel } from '../../../components/common/OwnerLabel/OwnerLabel.com
import TierCard from '../../../components/common/TierCard/TierCard';
import EntityHeaderTitle from '../../../components/Entity/EntityHeaderTitle/EntityHeaderTitle.component';
import { AUTO_PILOT_APP_NAME } from '../../../constants/Applications.constant';
import { DATA_ASSET_ICON_DIMENSION } from '../../../constants/constants';
import { SERVICE_TYPES } from '../../../constants/Services.constant';
import { TAG_START_WITH } from '../../../constants/Tag.constants';
import { useTourProvider } from '../../../context/TourProvider/TourProvider';
@ -53,6 +51,7 @@ import { getActiveAnnouncement } from '../../../rest/feedsAPI';
import { getDataQualityLineage } from '../../../rest/lineageAPI';
import { getContainerByName } from '../../../rest/storageAPI';
import {
ExtraInfoLabel,
getDataAssetsHeaderInfo,
getEntityExtraInfoLength,
isDataAssetsWithServiceField,
@ -91,89 +90,6 @@ import {
EntitiesWithDomainField,
} from './DataAssetsHeader.interface';
export const ExtraInfoLabel = ({
label,
value,
dataTestId,
inlineLayout = false,
}: {
label: string;
value: string | number | React.ReactNode;
dataTestId?: string;
inlineLayout?: boolean;
}) => {
if (inlineLayout) {
return (
<>
<Divider className="self-center" type="vertical" />
<Typography.Text
className="self-center text-xs whitespace-nowrap"
data-testid={dataTestId}>
{!isEmpty(label) && (
<span className="text-grey-muted">{`${label}: `}</span>
)}
<span className="font-medium">{value}</span>
</Typography.Text>
</>
);
}
return (
<div className="d-flex align-start extra-info-container">
<Typography.Text
className="whitespace-nowrap text-sm d-flex flex-col gap-2"
data-testid={dataTestId}>
{!isEmpty(label) && (
<span className="extra-info-label-heading">{label}</span>
)}
<div className={classNames('font-medium extra-info-value')}>
{value}
</div>
</Typography.Text>
</div>
);
};
export const ExtraInfoLink = ({
label,
value,
href,
newTab = false,
ellipsis = false,
}: {
label: string;
value: string | number;
href: string;
newTab?: boolean;
ellipsis?: boolean;
}) => (
<div
className={classNames('d-flex text-sm flex-col gap-2', {
'w-48': ellipsis,
})}>
{!isEmpty(label) && (
<span className="extra-info-label-heading m-r-xss">{label}</span>
)}
<div className="d-flex items-center gap-1">
<Tooltip title={value}>
<Typography.Link
ellipsis
className="extra-info-link"
href={href}
rel={newTab ? 'noopener noreferrer' : undefined}
target={newTab ? '_blank' : undefined}>
{value}
</Typography.Link>
</Tooltip>
<Icon
className="m-l-xs"
component={IconExternalLink}
style={DATA_ASSET_ICON_DIMENSION}
/>
</div>
</div>
);
export const DataAssetsHeader = ({
allowSoftDelete = true,
showDomain = true,
@ -298,9 +214,7 @@ export const DataAssetsHeader = ({
};
const alertBadge = useMemo(() => {
return tableClassBase.getAlertEnableStatus() &&
dqFailureCount > 0 &&
isDqAlertSupported ? (
const renderAlertBadgeWithDq = () => (
<Space size={8}>
{badge}
<Tooltip placement="right" title={t('label.check-upstream-failure')}>
@ -320,9 +234,16 @@ export const DataAssetsHeader = ({
</Link>
</Tooltip>
</Space>
) : (
badge
);
if (
isDqAlertSupported &&
tableClassBase.getAlertEnableStatus() &&
dqFailureCount > 0
) {
return renderAlertBadgeWithDq();
}
return badge;
}, [dqFailureCount, dataAsset?.fullyQualifiedName, entityType, badge]);
const fetchActiveAnnouncement = async () => {
@ -551,7 +472,7 @@ export const DataAssetsHeader = ({
)}
/>
<Row>
<Col flex="auto">
<Col className="w-min-0" flex="1">
<EntityHeaderTitle
badge={alertBadge}
deleted={dataAsset?.deleted}
@ -568,7 +489,7 @@ export const DataAssetsHeader = ({
serviceName={dataAssetServiceName}
/>
</Col>
<Col className="flex items-center">
<Col className="flex items-center ">
<Space className="">
<ButtonGroup
className="data-asset-button-group spaced"

View File

@ -21,7 +21,7 @@ import { MOCK_TIER_DATA } from '../../../mocks/TableData.mock';
import { getDataQualityLineage } from '../../../rest/lineageAPI';
import { getContainerByName } from '../../../rest/storageAPI';
import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils';
import { DataAssetsHeader, ExtraInfoLink } from './DataAssetsHeader.component';
import { DataAssetsHeader } from './DataAssetsHeader.component';
import { DataAssetsHeaderProps } from './DataAssetsHeader.interface';
import { AUTO_PILOT_APP_NAME } from '../../../constants/Applications.constant';
@ -30,6 +30,8 @@ import { DatabaseServiceType } from '../../../generated/entity/services/database
import { LabelType, State, TagSource } from '../../../generated/tests/testCase';
import { AssetCertification } from '../../../generated/type/assetCertification';
import { triggerOnDemandApp } from '../../../rest/applicationAPI';
import { ExtraInfoLink } from '../../../utils/DataAssetsHeader.utils';
const mockProps: DataAssetsHeaderProps = {
dataAsset: {
id: 'assets-id',
@ -87,6 +89,20 @@ jest.mock('../../../utils/DataAssetsHeader.utils', () => ({
})),
getEntityExtraInfoLength: jest.fn().mockImplementation(() => 0),
isDataAssetsWithServiceField: jest.fn().mockImplementation(() => true),
ExtraInfoLabel: jest.fn().mockImplementation(({ label, value }) => (
<div>
{label && <span>{label}</span>}
<span>{value}</span>
</div>
)),
ExtraInfoLink: jest.fn().mockImplementation(({ value, href, newTab }) => {
const props = {
href,
...(newTab ? { target: '_blank', rel: 'noopener noreferrer' } : {}),
};
return <a {...props}>{value}</a>;
}),
}));
jest.mock('../../common/CertificationTag/CertificationTag', () => {
@ -293,8 +309,6 @@ describe('DataAssetsHeader component', () => {
expect(sourceUrlLink).toHaveAttribute('href', mockSourceUrl);
expect(sourceUrlLink).toHaveAttribute('target', '_blank');
expect(screen.getByText('label.view-in-service-type')).toBeInTheDocument();
``;
});
it('should not render source URL button when sourceUrl is not present', () => {

View File

@ -21,12 +21,9 @@ jest.mock('../../../utils/ToastUtils', () => ({
showErrorToast: jest.fn(),
}));
jest.mock(
'../../DataAssets/DataAssetsHeader/DataAssetsHeader.component',
() => ({
ExtraInfoLabel: jest.fn().mockImplementation(({ value }) => value),
})
);
jest.mock('../../../utils/DataAssetsHeader.utils', () => ({
ExtraInfoLabel: jest.fn().mockImplementation(({ value }) => value),
}));
const mockOnUpdate = jest.fn();

View File

@ -112,8 +112,8 @@ const EntityHeaderTitle = ({
wrap={false}>
{icon && <Col className="flex-center">{icon}</Col>}
<Col
className={classNames('d-flex flex-col gap-1', {
'w-max-full-140': deleted || badge,
className={classNames('d-flex flex-col gap-1 w-min-0 ', {
'w-max-full-200': deleted || badge,
})}>
{/* If we do not have displayName name only be shown in the bold from the below code */}
{!isEmpty(displayName) && showName ? (

View File

@ -13,19 +13,16 @@
@import (reference) url('../../../styles/variables.less');
.entity-header-title {
flex: 1;
.ant-typography-ellipsis.entity-header-name {
margin-bottom: 0px;
display: block;
color: @grey-900;
max-width: 500px;
min-width: 0;
}
.ant-typography-ellipsis.entity-header-display-name {
font-size: 16px;
color: @grey-700;
max-width: 500px;
}
.ant-col.entity-header-content {
max-width: calc(100% - 100px);
}
}

View File

@ -417,18 +417,10 @@ export const SERVICE_TYPES_ENUM = {
};
export const BETA_SERVICES = [
DatabaseServiceType.BigTable,
DatabaseServiceType.SAS,
PipelineServiceType.Spline,
PipelineServiceType.OpenLineage,
PipelineServiceType.Flink,
PipelineServiceType.Wherescape,
DatabaseServiceType.Teradata,
StorageServiceType.Gcs,
DatabaseServiceType.SapERP,
DatabaseServiceType.Cassandra,
MetadataServiceType.AlationSink,
DatabaseServiceType.Synapse,
DatabaseServiceType.Cockroach,
SearchServiceType.OpenSearch,
];

View File

@ -127,14 +127,9 @@ jest.mock('../../components/common/OwnerLabel/OwnerLabel.component', () => ({
)),
}));
jest.mock(
'../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.component',
() => ({
ExtraInfoLabel: jest
.fn()
.mockImplementation(() => <div>ExtraInfoLabel</div>),
})
);
jest.mock('../../utils/DataAssetsHeader.utils', () => ({
ExtraInfoLabel: jest.fn().mockImplementation(() => <div>ExtraInfoLabel</div>),
}));
jest.mock('../../components/PageLayoutV1/PageLayoutV1', () =>
jest.fn().mockImplementation(({ children }) => <div>{children}</div>)

View File

@ -343,6 +343,12 @@ const TagPage = () => {
setPreviewAsset(undefined);
}
} catch (error) {
showErrorToast(
error as AxiosError,
t('server.entity-fetch-error', {
entity: t('label.asset-plural'),
})
);
setAssetCount(0);
}
};
@ -593,7 +599,7 @@ const TagPage = () => {
className="data-classification"
data-testid="data-classification"
gutter={[0, 12]}>
<Col className="p-x-md" flex="auto">
<Col className="p-x-md" flex="1">
<EntityHeader
badge={
tagItem.disabled && (

View File

@ -177,8 +177,8 @@
.w-max-full-45 {
max-width: calc(100% - 45px);
}
.w-max-full-140 {
max-width: calc(100% - 140px);
.w-max-full-200 {
max-width: calc(100% - 200px);
}
.w-max-stretch {
max-width: stretch;

View File

@ -63,7 +63,6 @@ import { AlertEventDetailsToDisplay } from '../../components/Alerts/AlertDetails
import TeamAndUserSelectItem from '../../components/Alerts/DestinationFormItem/TeamAndUserSelectItem/TeamAndUserSelectItem';
import { AsyncSelect } from '../../components/common/AsyncSelect/AsyncSelect';
import { InlineAlertProps } from '../../components/common/InlineAlert/InlineAlert.interface';
import { ExtraInfoLabel } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.component';
import {
DEFAULT_READ_TIMEOUT,
DESTINATION_DROPDOWN_TABS,
@ -105,11 +104,12 @@ import {
ModifiedEventSubscription,
} from '../../pages/AddObservabilityPage/AddObservabilityPage.interface';
import { searchData } from '../../rest/miscAPI';
import { ExtraInfoLabel } from '../DataAssetsHeader.utils';
import { getEntityName, getEntityNameLabel } from '../EntityUtils';
import { handleEntityCreationError } from '../formUtils';
import { getConfigFieldFromDestinationType } from '../ObservabilityUtils';
import searchClassBase from '../SearchClassBase';
import { showSuccessToast } from '../ToastUtils';
import { showErrorToast, showSuccessToast } from '../ToastUtils';
import './alerts-util.less';
export const getAlertsActionTypeIcon = (type?: SubscriptionType) => {
@ -265,6 +265,13 @@ export const searchEntity = async ({
'label'
);
} catch (error) {
showErrorToast(
error as AxiosError,
t('server.entity-fetch-error', {
entity: t('label.search'),
})
);
return [];
}
};
@ -352,26 +359,24 @@ export const getSupportedFilterOptions = (
}));
export const getConnectionTimeoutField = () => (
<>
<Row align="middle">
<Col span={7}>{`${t('label.connection-timeout')} (${t(
'label.second-plural'
)})`}</Col>
<Col span={1}>:</Col>
<Col data-testid="connection-timeout" span={16}>
<Form.Item name="timeout">
<Input
data-testid="connection-timeout-input"
defaultValue={10}
placeholder={`${t('label.connection-timeout')} (${t(
'label.second-plural'
)})`}
type="number"
/>
</Form.Item>
</Col>
</Row>
</>
<Row align="middle">
<Col span={7}>{`${t('label.connection-timeout')} (${t(
'label.second-plural'
)})`}</Col>
<Col span={1}>:</Col>
<Col data-testid="connection-timeout" span={16}>
<Form.Item name="timeout">
<Input
data-testid="connection-timeout-input"
defaultValue={10}
placeholder={`${t('label.connection-timeout')} (${t(
'label.second-plural'
)})`}
type="number"
/>
</Form.Item>
</Col>
</Row>
);
export const getReadTimeoutField = () => (

View File

@ -39,15 +39,11 @@ import {
getEntityExtraInfoLength,
} from './DataAssetsHeader.utils';
jest.mock(
'../components/DataAssets/DataAssetsHeader/DataAssetsHeader.component',
() => ({
ExtraInfoLabel: jest.fn().mockImplementation(({ value }) => {
value;
}),
ExtraInfoLink: jest.fn().mockImplementation(({ value }) => value),
})
);
jest.mock('./DataAssetsHeader.utils', () => ({
...jest.requireActual('./DataAssetsHeader.utils'),
ExtraInfoLabel: jest.fn().mockImplementation(({ value }) => value),
ExtraInfoLink: jest.fn().mockImplementation(({ value }) => value),
}));
jest.mock('./EntityUtils', () => ({
getEntityName: jest.fn().mockReturnValue('entityName'),
getEntityBreadcrumbs: jest.fn().mockReturnValue([

View File

@ -12,21 +12,23 @@
* limitations under the License.
*/
import { Divider } from 'antd';
import Icon from '@ant-design/icons';
import { Divider, Tooltip, Typography } from 'antd';
import classNames from 'classnames';
import { t } from 'i18next';
import { isArray, isObject, isUndefined } from 'lodash';
import { isArray, isEmpty, isObject, isUndefined } from 'lodash';
import React, { ReactNode } from 'react';
import {
ExtraInfoLabel,
ExtraInfoLink,
} from '../components/DataAssets/DataAssetsHeader/DataAssetsHeader.component';
import { ReactComponent as IconExternalLink } from '../assets/svg/external-links.svg';
import {
DataAssetHeaderInfo,
DataAssetsHeaderProps,
DataAssetsType,
DataAssetsWithServiceField,
} from '../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface';
import { NO_DATA_PLACEHOLDER } from '../constants/constants';
import {
DATA_ASSET_ICON_DIMENSION,
NO_DATA_PLACEHOLDER,
} from '../constants/constants';
import { EntityType } from '../enums/entity.enum';
import { APICollection } from '../generated/entity/data/apiCollection';
import { APIEndpoint } from '../generated/entity/data/apiEndpoint';
@ -64,6 +66,89 @@ import { getEntityDetailsPath } from './RouterUtils';
import { bytesToSize } from './StringsUtils';
import { getUsagePercentile } from './TableUtils';
export const ExtraInfoLabel = ({
label,
value,
dataTestId,
inlineLayout = false,
}: {
label: string;
value: string | number | React.ReactNode;
dataTestId?: string;
inlineLayout?: boolean;
}) => {
if (inlineLayout) {
return (
<>
<Divider className="self-center" type="vertical" />
<Typography.Text
className="self-center text-xs whitespace-nowrap"
data-testid={dataTestId}>
{!isEmpty(label) && (
<span className="text-grey-muted">{`${label}: `}</span>
)}
<span className="font-medium">{value}</span>
</Typography.Text>
</>
);
}
return (
<div className="d-flex align-start extra-info-container">
<Typography.Text
className="whitespace-nowrap text-sm d-flex flex-col gap-2"
data-testid={dataTestId}>
{!isEmpty(label) && (
<span className="extra-info-label-heading">{label}</span>
)}
<div className={classNames('font-medium extra-info-value')}>
{value}
</div>
</Typography.Text>
</div>
);
};
export const ExtraInfoLink = ({
label,
value,
href,
newTab = false,
ellipsis = false,
}: {
label: string;
value: string | number;
href: string;
newTab?: boolean;
ellipsis?: boolean;
}) => (
<div
className={classNames('d-flex text-sm flex-col gap-2', {
'w-48': ellipsis,
})}>
{!isEmpty(label) && (
<span className="extra-info-label-heading m-r-xss">{label}</span>
)}
<div className="d-flex items-center gap-1">
<Tooltip title={value}>
<Typography.Link
ellipsis
className="extra-info-link"
href={href}
rel={newTab ? 'noopener noreferrer' : undefined}
target={newTab ? '_blank' : undefined}>
{value}
</Typography.Link>
</Tooltip>
<Icon
className="m-l-xs"
component={IconExternalLink}
style={DATA_ASSET_ICON_DIMENSION}
/>
</div>
</div>
);
export const getDataAssetsHeaderInfo = (
entityType: DataAssetsHeaderProps['entityType'],
dataAsset: DataAssetsHeaderProps['dataAsset'],