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 React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Link, useHistory, useParams } from 'react-router-dom'; 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 RedAlertIcon } from '../../../assets/svg/ic-alert-red.svg';
import { ReactComponent as TaskOpenIcon } from '../../../assets/svg/ic-open-task.svg'; import { ReactComponent as TaskOpenIcon } from '../../../assets/svg/ic-open-task.svg';
import { ReactComponent as VersionIcon } from '../../../assets/svg/ic-version.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 TierCard from '../../../components/common/TierCard/TierCard';
import EntityHeaderTitle from '../../../components/Entity/EntityHeaderTitle/EntityHeaderTitle.component'; import EntityHeaderTitle from '../../../components/Entity/EntityHeaderTitle/EntityHeaderTitle.component';
import { AUTO_PILOT_APP_NAME } from '../../../constants/Applications.constant'; 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 { SERVICE_TYPES } from '../../../constants/Services.constant';
import { TAG_START_WITH } from '../../../constants/Tag.constants'; import { TAG_START_WITH } from '../../../constants/Tag.constants';
import { useTourProvider } from '../../../context/TourProvider/TourProvider'; import { useTourProvider } from '../../../context/TourProvider/TourProvider';
@ -53,6 +51,7 @@ import { getActiveAnnouncement } from '../../../rest/feedsAPI';
import { getDataQualityLineage } from '../../../rest/lineageAPI'; import { getDataQualityLineage } from '../../../rest/lineageAPI';
import { getContainerByName } from '../../../rest/storageAPI'; import { getContainerByName } from '../../../rest/storageAPI';
import { import {
ExtraInfoLabel,
getDataAssetsHeaderInfo, getDataAssetsHeaderInfo,
getEntityExtraInfoLength, getEntityExtraInfoLength,
isDataAssetsWithServiceField, isDataAssetsWithServiceField,
@ -91,89 +90,6 @@ import {
EntitiesWithDomainField, EntitiesWithDomainField,
} from './DataAssetsHeader.interface'; } 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 = ({ export const DataAssetsHeader = ({
allowSoftDelete = true, allowSoftDelete = true,
showDomain = true, showDomain = true,
@ -298,9 +214,7 @@ export const DataAssetsHeader = ({
}; };
const alertBadge = useMemo(() => { const alertBadge = useMemo(() => {
return tableClassBase.getAlertEnableStatus() && const renderAlertBadgeWithDq = () => (
dqFailureCount > 0 &&
isDqAlertSupported ? (
<Space size={8}> <Space size={8}>
{badge} {badge}
<Tooltip placement="right" title={t('label.check-upstream-failure')}> <Tooltip placement="right" title={t('label.check-upstream-failure')}>
@ -320,9 +234,16 @@ export const DataAssetsHeader = ({
</Link> </Link>
</Tooltip> </Tooltip>
</Space> </Space>
) : (
badge
); );
if (
isDqAlertSupported &&
tableClassBase.getAlertEnableStatus() &&
dqFailureCount > 0
) {
return renderAlertBadgeWithDq();
}
return badge;
}, [dqFailureCount, dataAsset?.fullyQualifiedName, entityType, badge]); }, [dqFailureCount, dataAsset?.fullyQualifiedName, entityType, badge]);
const fetchActiveAnnouncement = async () => { const fetchActiveAnnouncement = async () => {
@ -551,7 +472,7 @@ export const DataAssetsHeader = ({
)} )}
/> />
<Row> <Row>
<Col flex="auto"> <Col className="w-min-0" flex="1">
<EntityHeaderTitle <EntityHeaderTitle
badge={alertBadge} badge={alertBadge}
deleted={dataAsset?.deleted} deleted={dataAsset?.deleted}
@ -568,7 +489,7 @@ export const DataAssetsHeader = ({
serviceName={dataAssetServiceName} serviceName={dataAssetServiceName}
/> />
</Col> </Col>
<Col className="flex items-center"> <Col className="flex items-center ">
<Space className=""> <Space className="">
<ButtonGroup <ButtonGroup
className="data-asset-button-group spaced" 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 { getDataQualityLineage } from '../../../rest/lineageAPI';
import { getContainerByName } from '../../../rest/storageAPI'; import { getContainerByName } from '../../../rest/storageAPI';
import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils'; import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils';
import { DataAssetsHeader, ExtraInfoLink } from './DataAssetsHeader.component'; import { DataAssetsHeader } from './DataAssetsHeader.component';
import { DataAssetsHeaderProps } from './DataAssetsHeader.interface'; import { DataAssetsHeaderProps } from './DataAssetsHeader.interface';
import { AUTO_PILOT_APP_NAME } from '../../../constants/Applications.constant'; 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 { LabelType, State, TagSource } from '../../../generated/tests/testCase';
import { AssetCertification } from '../../../generated/type/assetCertification'; import { AssetCertification } from '../../../generated/type/assetCertification';
import { triggerOnDemandApp } from '../../../rest/applicationAPI'; import { triggerOnDemandApp } from '../../../rest/applicationAPI';
import { ExtraInfoLink } from '../../../utils/DataAssetsHeader.utils';
const mockProps: DataAssetsHeaderProps = { const mockProps: DataAssetsHeaderProps = {
dataAsset: { dataAsset: {
id: 'assets-id', id: 'assets-id',
@ -87,6 +89,20 @@ jest.mock('../../../utils/DataAssetsHeader.utils', () => ({
})), })),
getEntityExtraInfoLength: jest.fn().mockImplementation(() => 0), getEntityExtraInfoLength: jest.fn().mockImplementation(() => 0),
isDataAssetsWithServiceField: jest.fn().mockImplementation(() => true), 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', () => { jest.mock('../../common/CertificationTag/CertificationTag', () => {
@ -293,8 +309,6 @@ describe('DataAssetsHeader component', () => {
expect(sourceUrlLink).toHaveAttribute('href', mockSourceUrl); expect(sourceUrlLink).toHaveAttribute('href', mockSourceUrl);
expect(sourceUrlLink).toHaveAttribute('target', '_blank'); expect(sourceUrlLink).toHaveAttribute('target', '_blank');
expect(screen.getByText('label.view-in-service-type')).toBeInTheDocument(); expect(screen.getByText('label.view-in-service-type')).toBeInTheDocument();
``;
}); });
it('should not render source URL button when sourceUrl is not present', () => { 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(), showErrorToast: jest.fn(),
})); }));
jest.mock( jest.mock('../../../utils/DataAssetsHeader.utils', () => ({
'../../DataAssets/DataAssetsHeader/DataAssetsHeader.component',
() => ({
ExtraInfoLabel: jest.fn().mockImplementation(({ value }) => value), ExtraInfoLabel: jest.fn().mockImplementation(({ value }) => value),
}) }));
);
const mockOnUpdate = jest.fn(); const mockOnUpdate = jest.fn();

View File

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

View File

@ -13,19 +13,16 @@
@import (reference) url('../../../styles/variables.less'); @import (reference) url('../../../styles/variables.less');
.entity-header-title { .entity-header-title {
flex: 1;
.ant-typography-ellipsis.entity-header-name { .ant-typography-ellipsis.entity-header-name {
margin-bottom: 0px; margin-bottom: 0px;
display: block; display: block;
color: @grey-900; color: @grey-900;
max-width: 500px; min-width: 0;
} }
.ant-typography-ellipsis.entity-header-display-name { .ant-typography-ellipsis.entity-header-display-name {
font-size: 16px; font-size: 16px;
color: @grey-700; 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 = [ export const BETA_SERVICES = [
DatabaseServiceType.BigTable,
DatabaseServiceType.SAS,
PipelineServiceType.Spline,
PipelineServiceType.OpenLineage, PipelineServiceType.OpenLineage,
PipelineServiceType.Flink,
PipelineServiceType.Wherescape, PipelineServiceType.Wherescape,
DatabaseServiceType.Teradata,
StorageServiceType.Gcs,
DatabaseServiceType.SapERP,
DatabaseServiceType.Cassandra, DatabaseServiceType.Cassandra,
MetadataServiceType.AlationSink, MetadataServiceType.AlationSink,
DatabaseServiceType.Synapse,
DatabaseServiceType.Cockroach, DatabaseServiceType.Cockroach,
SearchServiceType.OpenSearch, SearchServiceType.OpenSearch,
]; ];

View File

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

View File

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

View File

@ -177,8 +177,8 @@
.w-max-full-45 { .w-max-full-45 {
max-width: calc(100% - 45px); max-width: calc(100% - 45px);
} }
.w-max-full-140 { .w-max-full-200 {
max-width: calc(100% - 140px); max-width: calc(100% - 200px);
} }
.w-max-stretch { .w-max-stretch {
max-width: 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 TeamAndUserSelectItem from '../../components/Alerts/DestinationFormItem/TeamAndUserSelectItem/TeamAndUserSelectItem';
import { AsyncSelect } from '../../components/common/AsyncSelect/AsyncSelect'; import { AsyncSelect } from '../../components/common/AsyncSelect/AsyncSelect';
import { InlineAlertProps } from '../../components/common/InlineAlert/InlineAlert.interface'; import { InlineAlertProps } from '../../components/common/InlineAlert/InlineAlert.interface';
import { ExtraInfoLabel } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.component';
import { import {
DEFAULT_READ_TIMEOUT, DEFAULT_READ_TIMEOUT,
DESTINATION_DROPDOWN_TABS, DESTINATION_DROPDOWN_TABS,
@ -105,11 +104,12 @@ import {
ModifiedEventSubscription, ModifiedEventSubscription,
} from '../../pages/AddObservabilityPage/AddObservabilityPage.interface'; } from '../../pages/AddObservabilityPage/AddObservabilityPage.interface';
import { searchData } from '../../rest/miscAPI'; import { searchData } from '../../rest/miscAPI';
import { ExtraInfoLabel } from '../DataAssetsHeader.utils';
import { getEntityName, getEntityNameLabel } from '../EntityUtils'; import { getEntityName, getEntityNameLabel } from '../EntityUtils';
import { handleEntityCreationError } from '../formUtils'; import { handleEntityCreationError } from '../formUtils';
import { getConfigFieldFromDestinationType } from '../ObservabilityUtils'; import { getConfigFieldFromDestinationType } from '../ObservabilityUtils';
import searchClassBase from '../SearchClassBase'; import searchClassBase from '../SearchClassBase';
import { showSuccessToast } from '../ToastUtils'; import { showErrorToast, showSuccessToast } from '../ToastUtils';
import './alerts-util.less'; import './alerts-util.less';
export const getAlertsActionTypeIcon = (type?: SubscriptionType) => { export const getAlertsActionTypeIcon = (type?: SubscriptionType) => {
@ -265,6 +265,13 @@ export const searchEntity = async ({
'label' 'label'
); );
} catch (error) { } catch (error) {
showErrorToast(
error as AxiosError,
t('server.entity-fetch-error', {
entity: t('label.search'),
})
);
return []; return [];
} }
}; };
@ -352,7 +359,6 @@ export const getSupportedFilterOptions = (
})); }));
export const getConnectionTimeoutField = () => ( export const getConnectionTimeoutField = () => (
<>
<Row align="middle"> <Row align="middle">
<Col span={7}>{`${t('label.connection-timeout')} (${t( <Col span={7}>{`${t('label.connection-timeout')} (${t(
'label.second-plural' 'label.second-plural'
@ -371,7 +377,6 @@ export const getConnectionTimeoutField = () => (
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>
</>
); );
export const getReadTimeoutField = () => ( export const getReadTimeoutField = () => (

View File

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

View File

@ -12,21 +12,23 @@
* limitations under the License. * 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 { t } from 'i18next';
import { isArray, isObject, isUndefined } from 'lodash'; import { isArray, isEmpty, isObject, isUndefined } from 'lodash';
import React, { ReactNode } from 'react'; import React, { ReactNode } from 'react';
import { import { ReactComponent as IconExternalLink } from '../assets/svg/external-links.svg';
ExtraInfoLabel,
ExtraInfoLink,
} from '../components/DataAssets/DataAssetsHeader/DataAssetsHeader.component';
import { import {
DataAssetHeaderInfo, DataAssetHeaderInfo,
DataAssetsHeaderProps, DataAssetsHeaderProps,
DataAssetsType, DataAssetsType,
DataAssetsWithServiceField, DataAssetsWithServiceField,
} from '../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; } 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 { EntityType } from '../enums/entity.enum';
import { APICollection } from '../generated/entity/data/apiCollection'; import { APICollection } from '../generated/entity/data/apiCollection';
import { APIEndpoint } from '../generated/entity/data/apiEndpoint'; import { APIEndpoint } from '../generated/entity/data/apiEndpoint';
@ -64,6 +66,89 @@ import { getEntityDetailsPath } from './RouterUtils';
import { bytesToSize } from './StringsUtils'; import { bytesToSize } from './StringsUtils';
import { getUsagePercentile } from './TableUtils'; 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 = ( export const getDataAssetsHeaderInfo = (
entityType: DataAssetsHeaderProps['entityType'], entityType: DataAssetsHeaderProps['entityType'],
dataAsset: DataAssetsHeaderProps['dataAsset'], dataAsset: DataAssetsHeaderProps['dataAsset'],