mirror of
https://github.com/datahub-project/datahub.git
synced 2025-11-02 19:58:59 +00:00
feat(ui) Use new Tabs component on entity profile pages (#13640)
This commit is contained in:
parent
b9b97e4bfe
commit
d505ed7c24
@ -7,7 +7,7 @@ import { Tooltip } from '@components/components/Tooltip';
|
||||
|
||||
import { colors } from '@src/alchemy-components/theme';
|
||||
|
||||
const StyledTabs = styled(AntTabs)`
|
||||
const StyledTabs = styled(AntTabs)<{ $addPaddingLeft?: boolean; $hideTabsHeader: boolean }>`
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
@ -17,9 +17,26 @@ const StyledTabs = styled(AntTabs)`
|
||||
color: ${colors.gray[600]};
|
||||
}
|
||||
|
||||
.ant-tabs-tab + .ant-tabs-tab {
|
||||
margin-left: 16px;
|
||||
}
|
||||
${({ $addPaddingLeft }) =>
|
||||
$addPaddingLeft
|
||||
? `
|
||||
.ant-tabs-tab {
|
||||
margin-left: 16px;
|
||||
}
|
||||
`
|
||||
: `
|
||||
.ant-tabs-tab + .ant-tabs-tab {
|
||||
margin-left: 16px;
|
||||
}
|
||||
`}
|
||||
|
||||
${({ $hideTabsHeader }) =>
|
||||
$hideTabsHeader &&
|
||||
`
|
||||
.ant-tabs-nav {
|
||||
display: none;
|
||||
}
|
||||
`}
|
||||
|
||||
.ant-tabs-tab-active .ant-tabs-tab-btn {
|
||||
color: ${(props) => props.theme.styles['primary-color']};
|
||||
@ -43,16 +60,17 @@ const StyledTabs = styled(AntTabs)`
|
||||
}
|
||||
`;
|
||||
|
||||
const TabViewWrapper = styled.div`
|
||||
const TabViewWrapper = styled.div<{ $disabled?: boolean }>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: 4px;
|
||||
${({ $disabled }) => $disabled && `color: ${colors.gray[1800]};`}
|
||||
`;
|
||||
|
||||
function TabView({ tab }: { tab: Tab }) {
|
||||
return (
|
||||
<Tooltip title={tab.tooltip}>
|
||||
<TabViewWrapper id={tab.id}>
|
||||
<TabViewWrapper id={tab.id} $disabled={tab.disabled} data-testid={tab.dataTestId}>
|
||||
{tab.name}
|
||||
{!!tab.count && <Pill label={`${tab.count}`} size="xs" color="violet" />}
|
||||
</TabViewWrapper>
|
||||
@ -68,6 +86,8 @@ export interface Tab {
|
||||
onSelectTab?: () => void;
|
||||
tooltip?: string;
|
||||
id?: string;
|
||||
dataTestId?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
@ -78,6 +98,8 @@ export interface Props {
|
||||
onUrlChange?: (url: string) => void;
|
||||
defaultTab?: string;
|
||||
getCurrentUrl?: () => string;
|
||||
addPaddingLeft?: boolean;
|
||||
hideTabsHeader?: boolean;
|
||||
}
|
||||
|
||||
export function Tabs({
|
||||
@ -88,6 +110,8 @@ export function Tabs({
|
||||
onUrlChange = (url) => window.history.replaceState({}, '', url),
|
||||
defaultTab,
|
||||
getCurrentUrl = () => window.location.pathname,
|
||||
addPaddingLeft,
|
||||
hideTabsHeader,
|
||||
}: Props) {
|
||||
const { TabPane } = AntTabs;
|
||||
|
||||
@ -128,10 +152,15 @@ export function Tabs({
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledTabs activeKey={selectedTab} onChange={handleTabClick}>
|
||||
<StyledTabs
|
||||
activeKey={selectedTab}
|
||||
onChange={handleTabClick}
|
||||
$addPaddingLeft={addPaddingLeft}
|
||||
$hideTabsHeader={!!hideTabsHeader}
|
||||
>
|
||||
{tabs.map((tab) => {
|
||||
return (
|
||||
<TabPane tab={<TabView tab={tab} />} key={tab.key}>
|
||||
<TabPane tab={<TabView tab={tab} />} key={tab.key} disabled={tab.disabled}>
|
||||
{tab.component}
|
||||
</TabPane>
|
||||
);
|
||||
|
||||
@ -27,7 +27,6 @@ import { getDataForEntityType } from '@app/entityV2/shared/containers/profile/ut
|
||||
import SidebarNotesSection from '@app/entityV2/shared/sidebarSection/SidebarNotesSection';
|
||||
import SidebarStructuredProperties from '@app/entityV2/shared/sidebarSection/SidebarStructuredProperties';
|
||||
import { DocumentationTab } from '@app/entityV2/shared/tabs/Documentation/DocumentationTab';
|
||||
import TabNameWithCount from '@app/entityV2/shared/tabs/Entity/TabNameWithCount';
|
||||
import { PropertiesTab } from '@app/entityV2/shared/tabs/Properties/PropertiesTab';
|
||||
|
||||
import { useGetApplicationQuery } from '@graphql/application.generated';
|
||||
@ -115,9 +114,8 @@ export class ApplicationEntity implements Entity<Application> {
|
||||
},
|
||||
{
|
||||
name: 'Assets',
|
||||
getDynamicName: (entityData, _, loading) => {
|
||||
const assetCount = entityData?.children?.total;
|
||||
return <TabNameWithCount name="Assets" count={assetCount} loading={loading} />;
|
||||
getCount: (entityData, _, loading) => {
|
||||
return !loading ? entityData?.children?.total : undefined;
|
||||
},
|
||||
component: ApplicationEntitiesTab,
|
||||
icon: AppstoreOutlined,
|
||||
|
||||
@ -9,6 +9,7 @@ import TabToolbar from '@app/entity/shared/components/styled/TabToolbar';
|
||||
import { ANTD_GRAY } from '@app/entity/shared/constants';
|
||||
import { TestResults } from '@app/entity/shared/tabs/Dataset/Governance/TestResults';
|
||||
import { useGetValidationsTab } from '@app/entity/shared/tabs/Dataset/Validations/useGetValidationsTab';
|
||||
import { GOVERNANCE_TAB_NAME } from '@app/entityV2/dataset/constants';
|
||||
|
||||
const TabTitle = styled.span`
|
||||
margin-left: 4px;
|
||||
@ -41,7 +42,7 @@ export const GovernanceTab = () => {
|
||||
|
||||
// If no tab was selected, select a default tab.
|
||||
useEffect(() => {
|
||||
if (!selectedTab) {
|
||||
if (!selectedTab && basePath.endsWith(GOVERNANCE_TAB_NAME)) {
|
||||
// Route to the default tab.
|
||||
history.replace(`${basePath}/${DEFAULT_TAB}`);
|
||||
}
|
||||
|
||||
@ -22,7 +22,6 @@ import { EntityActionItem } from '@app/entityV2/shared/entity/EntityActions';
|
||||
import SidebarNotesSection from '@app/entityV2/shared/sidebarSection/SidebarNotesSection';
|
||||
import SidebarStructuredProperties from '@app/entityV2/shared/sidebarSection/SidebarStructuredProperties';
|
||||
import { DocumentationTab } from '@app/entityV2/shared/tabs/Documentation/DocumentationTab';
|
||||
import TabNameWithCount from '@app/entityV2/shared/tabs/Entity/TabNameWithCount';
|
||||
import { PropertiesTab } from '@app/entityV2/shared/tabs/Properties/PropertiesTab';
|
||||
|
||||
import { useGetApplicationQuery } from '@graphql/application.generated';
|
||||
@ -106,9 +105,8 @@ export class ApplicationEntity implements Entity<Application> {
|
||||
},
|
||||
{
|
||||
name: 'Assets',
|
||||
getDynamicName: (entityData, _, loading) => {
|
||||
const assetCount = entityData?.children?.total;
|
||||
return <TabNameWithCount name="Assets" count={assetCount} loading={loading} />;
|
||||
getCount: (entityData, _, loading) => {
|
||||
return !loading ? entityData?.children?.total : undefined;
|
||||
},
|
||||
component: ApplicationEntitiesTab,
|
||||
icon: AppstoreOutlined,
|
||||
|
||||
@ -39,7 +39,6 @@ import { DocumentationTab } from '@app/entityV2/shared/tabs/Documentation/Docume
|
||||
import { EmbedTab } from '@app/entityV2/shared/tabs/Embed/EmbedTab';
|
||||
import { ChartDashboardsTab } from '@app/entityV2/shared/tabs/Entity/ChartDashboardsTab';
|
||||
import { InputFieldsTab } from '@app/entityV2/shared/tabs/Entity/InputFieldsTab';
|
||||
import TabNameWithCount from '@app/entityV2/shared/tabs/Entity/TabNameWithCount';
|
||||
import { IncidentTab } from '@app/entityV2/shared/tabs/Incident/IncidentTab';
|
||||
import { LineageTab } from '@app/entityV2/shared/tabs/Lineage/LineageTab';
|
||||
import { PropertiesTab } from '@app/entityV2/shared/tabs/Properties/PropertiesTab';
|
||||
@ -197,9 +196,8 @@ export class ChartEntity implements Entity<Chart> {
|
||||
},
|
||||
{
|
||||
name: 'Incidents',
|
||||
getDynamicName: (_, chart, loading) => {
|
||||
const activeIncidentCount = chart?.chart?.activeIncidents?.total;
|
||||
return <TabNameWithCount name="Incidents" count={activeIncidentCount} loading={loading} />;
|
||||
getCount: (_, chart, loading) => {
|
||||
return !loading ? chart?.chart?.activeIncidents?.total : undefined;
|
||||
},
|
||||
icon: WarningOutlined,
|
||||
component: IncidentTab,
|
||||
|
||||
@ -40,7 +40,6 @@ import { DocumentationTab } from '@app/entityV2/shared/tabs/Documentation/Docume
|
||||
import { EmbedTab } from '@app/entityV2/shared/tabs/Embed/EmbedTab';
|
||||
import { DashboardChartsTab } from '@app/entityV2/shared/tabs/Entity/DashboardChartsTab';
|
||||
import { DashboardDatasetsTab } from '@app/entityV2/shared/tabs/Entity/DashboardDatasetsTab';
|
||||
import TabNameWithCount from '@app/entityV2/shared/tabs/Entity/TabNameWithCount';
|
||||
import { IncidentTab } from '@app/entityV2/shared/tabs/Incident/IncidentTab';
|
||||
import { LineageTab } from '@app/entityV2/shared/tabs/Lineage/LineageTab';
|
||||
import { PropertiesTab } from '@app/entityV2/shared/tabs/Properties/PropertiesTab';
|
||||
@ -193,9 +192,8 @@ export class DashboardEntity implements Entity<Dashboard> {
|
||||
name: 'Incidents',
|
||||
icon: WarningOutlined,
|
||||
component: IncidentTab,
|
||||
getDynamicName: (_, dashboard, loading) => {
|
||||
const activeIncidentCount = dashboard?.dashboard?.activeIncidents?.total;
|
||||
return <TabNameWithCount name="Incidents" count={activeIncidentCount} loading={loading} />;
|
||||
getCount: (_, dashboard) => {
|
||||
return dashboard?.dashboard?.activeIncidents?.total;
|
||||
},
|
||||
},
|
||||
]}
|
||||
|
||||
@ -22,7 +22,6 @@ import SidebarNotesSection from '@app/entityV2/shared/sidebarSection/SidebarNote
|
||||
import SidebarStructuredProperties from '@app/entityV2/shared/sidebarSection/SidebarStructuredProperties';
|
||||
import { DocumentationTab } from '@app/entityV2/shared/tabs/Documentation/DocumentationTab';
|
||||
import { DataFlowJobsTab } from '@app/entityV2/shared/tabs/Entity/DataFlowJobsTab';
|
||||
import TabNameWithCount from '@app/entityV2/shared/tabs/Entity/TabNameWithCount';
|
||||
import { IncidentTab } from '@app/entityV2/shared/tabs/Incident/IncidentTab';
|
||||
import { PropertiesTab } from '@app/entityV2/shared/tabs/Properties/PropertiesTab';
|
||||
import { getDataProduct, isOutputPort } from '@app/entityV2/shared/utils';
|
||||
@ -110,9 +109,8 @@ export class DataFlowEntity implements Entity<DataFlow> {
|
||||
name: 'Incidents',
|
||||
icon: WarningCircle,
|
||||
component: IncidentTab,
|
||||
getDynamicName: (_, dataFlow, loading) => {
|
||||
const activeIncidentCount = dataFlow?.dataFlow?.activeIncidents?.total;
|
||||
return <TabNameWithCount name="Incidents" count={activeIncidentCount} loading={loading} />;
|
||||
getCount: (_, dataFlow) => {
|
||||
return dataFlow?.dataFlow?.activeIncidents?.total;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@ -27,7 +27,6 @@ import SidebarNotesSection from '@app/entityV2/shared/sidebarSection/SidebarNote
|
||||
import SidebarStructuredProperties from '@app/entityV2/shared/sidebarSection/SidebarStructuredProperties';
|
||||
import { DocumentationTab } from '@app/entityV2/shared/tabs/Documentation/DocumentationTab';
|
||||
import { DataJobFlowTab } from '@app/entityV2/shared/tabs/Entity/DataJobFlowTab';
|
||||
import TabNameWithCount from '@app/entityV2/shared/tabs/Entity/TabNameWithCount';
|
||||
import { IncidentTab } from '@app/entityV2/shared/tabs/Incident/IncidentTab';
|
||||
import { LineageTab } from '@app/entityV2/shared/tabs/Lineage/LineageTab';
|
||||
import { PropertiesTab } from '@app/entityV2/shared/tabs/Properties/PropertiesTab';
|
||||
@ -142,9 +141,8 @@ export class DataJobEntity implements Entity<DataJob> {
|
||||
name: 'Incidents',
|
||||
icon: WarningCircle,
|
||||
component: IncidentTab,
|
||||
getDynamicName: (_, dataJob, loading) => {
|
||||
const activeIncidentCount = dataJob?.dataJob?.activeIncidents?.total;
|
||||
return <TabNameWithCount name="Incidents" count={activeIncidentCount} loading={loading} />;
|
||||
getCount: (_, dataJob) => {
|
||||
return dataJob?.dataJob?.activeIncidents?.total;
|
||||
},
|
||||
},
|
||||
]}
|
||||
|
||||
@ -30,7 +30,6 @@ import { EntityActionItem } from '@app/entityV2/shared/entity/EntityActions';
|
||||
import SidebarNotesSection from '@app/entityV2/shared/sidebarSection/SidebarNotesSection';
|
||||
import SidebarStructuredProperties from '@app/entityV2/shared/sidebarSection/SidebarStructuredProperties';
|
||||
import { DocumentationTab } from '@app/entityV2/shared/tabs/Documentation/DocumentationTab';
|
||||
import TabNameWithCount from '@app/entityV2/shared/tabs/Entity/TabNameWithCount';
|
||||
import { PropertiesTab } from '@app/entityV2/shared/tabs/Properties/PropertiesTab';
|
||||
|
||||
import { useGetDataProductQuery } from '@graphql/dataProduct.generated';
|
||||
@ -118,9 +117,8 @@ export class DataProductEntity implements Entity<DataProduct> {
|
||||
},
|
||||
{
|
||||
name: 'Assets',
|
||||
getDynamicName: (entityData, _, loading) => {
|
||||
const assetCount = entityData?.entities?.total;
|
||||
return <TabNameWithCount name="Assets" count={assetCount} loading={loading} />;
|
||||
getCount: (entityData, _) => {
|
||||
return entityData?.entities?.total;
|
||||
},
|
||||
component: DataProductEntitiesTab,
|
||||
icon: AppstoreOutlined,
|
||||
|
||||
@ -17,9 +17,11 @@ import * as React from 'react';
|
||||
|
||||
import { GenericEntityProperties } from '@app/entity/shared/types';
|
||||
import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entityV2/Entity';
|
||||
import { GOVERNANCE_TAB_NAME, QUALITY_TAB_NAME } from '@app/entityV2/dataset/constants';
|
||||
import { Preview } from '@app/entityV2/dataset/preview/Preview';
|
||||
import { OperationsTab } from '@app/entityV2/dataset/profile/OperationsTab';
|
||||
import { DatasetStatsSummarySubHeader } from '@app/entityV2/dataset/profile/stats/stats/DatasetStatsSummarySubHeader';
|
||||
import { useGetColumnTabCount } from '@app/entityV2/dataset/profile/useGetColumnTabCount';
|
||||
import { EntityMenuItems } from '@app/entityV2/shared/EntityDropdown/EntityMenuActions';
|
||||
import { SubType, TYPE_ICON_CLASS_NAME } from '@app/entityV2/shared/components/subtypes';
|
||||
import { EntityProfile } from '@app/entityV2/shared/containers/profile/EntityProfile';
|
||||
@ -49,8 +51,6 @@ import { AcrylValidationsTab } from '@app/entityV2/shared/tabs/Dataset/Validatio
|
||||
import ViewDefinitionTab from '@app/entityV2/shared/tabs/Dataset/View/ViewDefinitionTab';
|
||||
import { DocumentationTab } from '@app/entityV2/shared/tabs/Documentation/DocumentationTab';
|
||||
import { EmbedTab } from '@app/entityV2/shared/tabs/Embed/EmbedTab';
|
||||
import ColumnTabNameHeader from '@app/entityV2/shared/tabs/Entity/ColumnTabNameHeader';
|
||||
import TabNameWithCount from '@app/entityV2/shared/tabs/Entity/TabNameWithCount';
|
||||
import { IncidentTab } from '@app/entityV2/shared/tabs/Incident/IncidentTab';
|
||||
import { LineageTab } from '@app/entityV2/shared/tabs/Lineage/LineageTab';
|
||||
import { PropertiesTab } from '@app/entityV2/shared/tabs/Properties/PropertiesTab';
|
||||
@ -156,7 +156,7 @@ export class DatasetEntity implements Entity<Dataset> {
|
||||
name: 'Columns',
|
||||
component: SchemaTab,
|
||||
icon: LayoutOutlined,
|
||||
getDynamicName: ColumnTabNameHeader,
|
||||
getCount: useGetColumnTabCount,
|
||||
},
|
||||
{
|
||||
name: 'View Definition',
|
||||
@ -203,12 +203,12 @@ export class DatasetEntity implements Entity<Dataset> {
|
||||
name: 'Properties',
|
||||
component: PropertiesTab,
|
||||
icon: UnorderedListOutlined,
|
||||
getDynamicName: (_, dataset: GetDatasetQuery, loading) => {
|
||||
getCount: (_, dataset: GetDatasetQuery) => {
|
||||
const customPropertiesCount = dataset?.dataset?.properties?.customProperties?.length || 0;
|
||||
const structuredPropertiesCount =
|
||||
dataset?.dataset?.structuredProperties?.properties?.length || 0;
|
||||
const propertiesCount = customPropertiesCount + structuredPropertiesCount;
|
||||
return <TabNameWithCount name="Properties" count={propertiesCount} loading={loading} />;
|
||||
return propertiesCount;
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -234,12 +234,12 @@ export class DatasetEntity implements Entity<Dataset> {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Quality',
|
||||
name: QUALITY_TAB_NAME,
|
||||
component: AcrylValidationsTab, // Use SaaS specific Validations Tab.
|
||||
icon: CheckCircleOutlined,
|
||||
},
|
||||
{
|
||||
name: 'Governance',
|
||||
name: GOVERNANCE_TAB_NAME,
|
||||
icon: () => (
|
||||
<span
|
||||
style={{
|
||||
@ -268,9 +268,8 @@ export class DatasetEntity implements Entity<Dataset> {
|
||||
name: 'Incidents',
|
||||
icon: WarningOutlined,
|
||||
component: IncidentTab,
|
||||
getDynamicName: (_, dataset, loading) => {
|
||||
const activeIncidentCount = dataset?.dataset?.activeIncidents?.total;
|
||||
return <TabNameWithCount name="Incidents" count={activeIncidentCount} loading={loading} />;
|
||||
getCount: (_, dataset) => {
|
||||
return dataset?.dataset?.activeIncidents?.total;
|
||||
},
|
||||
},
|
||||
]}
|
||||
|
||||
2
datahub-web-react/src/app/entityV2/dataset/constants.ts
Normal file
2
datahub-web-react/src/app/entityV2/dataset/constants.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export const GOVERNANCE_TAB_NAME = 'Governance';
|
||||
export const QUALITY_TAB_NAME = 'Quality';
|
||||
@ -0,0 +1,8 @@
|
||||
import { useGetEntityWithSchema } from '@app/entityV2/shared/tabs/Dataset/Schema/useGetEntitySchema';
|
||||
|
||||
export const useGetColumnTabCount = () => {
|
||||
const { entityWithSchema, loading } = useGetEntityWithSchema();
|
||||
const fieldsCount = entityWithSchema?.schemaMetadata?.fields?.length || 0;
|
||||
|
||||
return !loading ? fieldsCount : undefined;
|
||||
};
|
||||
@ -22,7 +22,6 @@ import SidebarNotesSection from '@app/entityV2/shared/sidebarSection/SidebarNote
|
||||
import SidebarStructuredProperties from '@app/entityV2/shared/sidebarSection/SidebarStructuredProperties';
|
||||
import { SUMMARY_TAB_ICON } from '@app/entityV2/shared/summary/HeaderComponents';
|
||||
import { DocumentationTab } from '@app/entityV2/shared/tabs/Documentation/DocumentationTab';
|
||||
import TabNameWithCount from '@app/entityV2/shared/tabs/Entity/TabNameWithCount';
|
||||
import { PropertiesTab } from '@app/entityV2/shared/tabs/Properties/PropertiesTab';
|
||||
|
||||
import { useGetDomainQuery } from '@graphql/domain.generated';
|
||||
@ -111,9 +110,8 @@ export class DomainEntity implements Entity<Domain> {
|
||||
{
|
||||
id: EntityProfileTab.DOMAIN_ENTITIES_TAB,
|
||||
name: 'Assets',
|
||||
getDynamicName: (entityData, _, loading) => {
|
||||
const assetCount = entityData?.entities?.total;
|
||||
return <TabNameWithCount name="Assets" count={assetCount} loading={loading} />;
|
||||
getCount: (entityData, _) => {
|
||||
return entityData?.entities?.total;
|
||||
},
|
||||
component: DomainEntitiesTab,
|
||||
icon: AppstoreOutlined,
|
||||
@ -127,9 +125,8 @@ export class DomainEntity implements Entity<Domain> {
|
||||
{
|
||||
id: EntityProfileTab.DATA_PRODUCTS_TAB,
|
||||
name: 'Data Products',
|
||||
getDynamicName: (entityData, _, loading) => {
|
||||
const dataProductsCount = entityData?.dataProducts?.total;
|
||||
return <TabNameWithCount name="Data Products" count={dataProductsCount} loading={loading} />;
|
||||
getCount: (entityData, _) => {
|
||||
return entityData?.dataProducts?.total;
|
||||
},
|
||||
component: DataProductsTab,
|
||||
icon: FileDoneOutlined,
|
||||
|
||||
@ -4,11 +4,11 @@ import * as React from 'react';
|
||||
|
||||
import { GenericEntityProperties } from '@app/entity/shared/types';
|
||||
import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entityV2/Entity';
|
||||
import GlossaryRelatedAssetsTabHeader from '@app/entityV2/glossaryTerm/GlossaryRelatedAssetsTabHeader';
|
||||
import { Preview } from '@app/entityV2/glossaryTerm/preview/Preview';
|
||||
import GlossaryRelatedEntity from '@app/entityV2/glossaryTerm/profile/GlossaryRelatedEntity';
|
||||
import GlossayRelatedTerms from '@app/entityV2/glossaryTerm/profile/GlossaryRelatedTerms';
|
||||
import { RelatedTermTypes } from '@app/entityV2/glossaryTerm/profile/GlossaryRelatedTermsResult';
|
||||
import useGlossaryRelatedAssetsTabCount from '@app/entityV2/glossaryTerm/profile/useGlossaryRelatedAssetsTabCount';
|
||||
import { EntityMenuItems } from '@app/entityV2/shared/EntityDropdown/EntityMenuActions';
|
||||
import { TYPE_ICON_CLASS_NAME } from '@app/entityV2/shared/components/subtypes';
|
||||
import { EntityProfile } from '@app/entityV2/shared/containers/profile/EntityProfile';
|
||||
@ -24,7 +24,6 @@ import SidebarNotesSection from '@app/entityV2/shared/sidebarSection/SidebarNote
|
||||
import SidebarStructuredProperties from '@app/entityV2/shared/sidebarSection/SidebarStructuredProperties';
|
||||
import { SchemaTab } from '@app/entityV2/shared/tabs/Dataset/Schema/SchemaTab';
|
||||
import { DocumentationTab } from '@app/entityV2/shared/tabs/Documentation/DocumentationTab';
|
||||
import TabNameWithCount from '@app/entityV2/shared/tabs/Entity/TabNameWithCount';
|
||||
import { PropertiesTab } from '@app/entityV2/shared/tabs/Properties/PropertiesTab';
|
||||
import { FetchedEntity } from '@app/lineage/types';
|
||||
|
||||
@ -110,7 +109,7 @@ export class GlossaryTermEntity implements Entity<GlossaryTerm> {
|
||||
},
|
||||
{
|
||||
name: 'Related Assets',
|
||||
getDynamicName: GlossaryRelatedAssetsTabHeader,
|
||||
getCount: useGlossaryRelatedAssetsTabCount,
|
||||
component: GlossaryRelatedEntity,
|
||||
icon: AppstoreOutlined,
|
||||
},
|
||||
@ -130,13 +129,11 @@ export class GlossaryTermEntity implements Entity<GlossaryTerm> {
|
||||
},
|
||||
{
|
||||
name: 'Related Terms',
|
||||
getDynamicName: (entityData, _, loading) => {
|
||||
getCount: (entityData, _, loading) => {
|
||||
const totalRelatedTerms = Object.keys(RelatedTermTypes).reduce((acc, curr) => {
|
||||
return acc + (entityData?.[curr]?.total || 0);
|
||||
}, 0);
|
||||
return (
|
||||
<TabNameWithCount name="Related Terms" count={totalRelatedTerms} loading={loading} />
|
||||
);
|
||||
return !loading ? totalRelatedTerms : undefined;
|
||||
},
|
||||
component: GlossayRelatedTerms,
|
||||
icon: () => <BookmarkSimple style={{ marginRight: 6 }} />,
|
||||
|
||||
@ -1,21 +1,7 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Pill } from '@src/alchemy-components';
|
||||
import { useEntityData } from '@src/app/entity/shared/EntityContext';
|
||||
import { formatNumber } from '@src/app/shared/formatNumber';
|
||||
import { useGetSearchResultsForMultipleQuery } from '@src/graphql/search.generated';
|
||||
|
||||
const Styled = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const TabName = styled.div`
|
||||
padding-right: 4px;
|
||||
`;
|
||||
|
||||
function GlossaryRelatedAssetsTabHeader() {
|
||||
export default function useGlossaryRelatedAssetsTabCount() {
|
||||
const { entityData } = useEntityData();
|
||||
|
||||
// To get the number of related assets
|
||||
@ -44,12 +30,5 @@ function GlossaryRelatedAssetsTabHeader() {
|
||||
fetchPolicy: 'cache-and-network',
|
||||
});
|
||||
|
||||
return (
|
||||
<Styled>
|
||||
<TabName>Related Assets</TabName>
|
||||
<Pill label={formatNumber(data?.searchAcrossEntities?.total || 0)} />
|
||||
</Styled>
|
||||
);
|
||||
return data?.searchAcrossEntities?.total || 0;
|
||||
}
|
||||
|
||||
export default GlossaryRelatedAssetsTabHeader;
|
||||
@ -21,7 +21,6 @@ import { getDataForEntityType } from '@app/entityV2/shared/containers/profile/ut
|
||||
import SidebarNotesSection from '@app/entityV2/shared/sidebarSection/SidebarNotesSection';
|
||||
import SidebarStructuredProperties from '@app/entityV2/shared/sidebarSection/SidebarStructuredProperties';
|
||||
import { DocumentationTab } from '@app/entityV2/shared/tabs/Documentation/DocumentationTab';
|
||||
import TabNameWithCount from '@app/entityV2/shared/tabs/Entity/TabNameWithCount';
|
||||
import { LineageTab } from '@app/entityV2/shared/tabs/Lineage/LineageTab';
|
||||
import { FeatureTableTab } from '@app/entityV2/shared/tabs/ML/MlFeatureFeatureTableTab';
|
||||
import { PropertiesTab } from '@app/entityV2/shared/tabs/Properties/PropertiesTab';
|
||||
@ -128,9 +127,8 @@ export class MLFeatureEntity implements Entity<MlFeature> {
|
||||
name: 'Incidents',
|
||||
icon: WarningCircle,
|
||||
component: IncidentTab,
|
||||
getDynamicName: (_, mlFeature, loading) => {
|
||||
const activeIncidentCount = mlFeature?.mlFeature?.activeIncidents?.total;
|
||||
return <TabNameWithCount name="Incidents" count={activeIncidentCount} loading={loading} />;
|
||||
getCount: (_, mlFeature) => {
|
||||
return mlFeature?.mlFeature?.activeIncidents?.total;
|
||||
},
|
||||
},
|
||||
]}
|
||||
|
||||
@ -68,8 +68,7 @@ export default function SourcesView() {
|
||||
</ViewRawButtonContainer>
|
||||
</div>
|
||||
<List
|
||||
style={{ marginTop: '24px', padding: '16px 32px' }}
|
||||
bordered
|
||||
style={{ padding: '0 32px 16px 32px' }}
|
||||
dataSource={sources}
|
||||
header={<Typography.Title level={3}>Sources</Typography.Title>}
|
||||
renderItem={(item) => (
|
||||
|
||||
@ -24,7 +24,6 @@ import { getDataForEntityType } from '@app/entityV2/shared/containers/profile/ut
|
||||
import SidebarNotesSection from '@app/entityV2/shared/sidebarSection/SidebarNotesSection';
|
||||
import SidebarStructuredProperties from '@app/entityV2/shared/sidebarSection/SidebarStructuredProperties';
|
||||
import { DocumentationTab } from '@app/entityV2/shared/tabs/Documentation/DocumentationTab';
|
||||
import TabNameWithCount from '@app/entityV2/shared/tabs/Entity/TabNameWithCount';
|
||||
import { IncidentTab } from '@app/entityV2/shared/tabs/Incident/IncidentTab';
|
||||
import { LineageTab } from '@app/entityV2/shared/tabs/Lineage/LineageTab';
|
||||
import { PropertiesTab } from '@app/entityV2/shared/tabs/Properties/PropertiesTab';
|
||||
@ -135,9 +134,8 @@ export class MLModelEntity implements Entity<MlModel> {
|
||||
name: 'Incidents',
|
||||
icon: WarningOutlined,
|
||||
component: IncidentTab,
|
||||
getDynamicName: (_, mlModel, loading) => {
|
||||
const activeIncidentCount = mlModel?.mlModel?.activeIncidents?.total;
|
||||
return <TabNameWithCount name="Incidents" count={activeIncidentCount} loading={loading} />;
|
||||
getCount: (_, mlModel) => {
|
||||
return mlModel?.mlModel?.activeIncidents?.total;
|
||||
},
|
||||
},
|
||||
]}
|
||||
|
||||
@ -30,13 +30,7 @@ import {
|
||||
import { EntityActionItem } from '@app/entityV2/shared/entity/EntityActions';
|
||||
import NonExistentEntityPage from '@app/entityV2/shared/entity/NonExistentEntityPage';
|
||||
import DynamicTab from '@app/entityV2/shared/tabs/Entity/weaklyTypedAspects/DynamicTab';
|
||||
import {
|
||||
EntitySidebarSection,
|
||||
EntitySidebarTab,
|
||||
EntityTab,
|
||||
TabContextType,
|
||||
TabRenderType,
|
||||
} from '@app/entityV2/shared/types';
|
||||
import { EntitySidebarSection, EntitySidebarTab, EntityTab, TabContextType } from '@app/entityV2/shared/types';
|
||||
import { useIsSeparateSiblingsMode } from '@app/entityV2/shared/useIsSeparateSiblingsMode';
|
||||
import VersionsDrawer from '@app/entityV2/shared/versioning/VersionsDrawer';
|
||||
import LineageExplorer from '@app/lineage/LineageExplorer';
|
||||
@ -155,6 +149,7 @@ const Body = styled.div<{ $isShowNavBarRedesign?: boolean }>`
|
||||
`;
|
||||
|
||||
const BodyContent = styled.div<{ $isShowNavBarRedesign?: boolean }>`
|
||||
padding-top: 12px;
|
||||
background-color: #ffffff;
|
||||
border-radius: ${(props) =>
|
||||
props.$isShowNavBarRedesign ? props.theme.styles['border-radius-navbar-redesign'] : '8px'};
|
||||
@ -169,15 +164,6 @@ const BodyContent = styled.div<{ $isShowNavBarRedesign?: boolean }>`
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const TabsWrapper = styled.div``;
|
||||
|
||||
const TabContent = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
const StyledAlert = styled(Alert)`
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
@ -406,20 +392,7 @@ export const EntityProfile = <T, U>({
|
||||
)}
|
||||
<Body $isShowNavBarRedesign={isShowNavBarRedesign}>
|
||||
<BodyContent $isShowNavBarRedesign={isShowNavBarRedesign}>
|
||||
{!isTabFullsize && (
|
||||
<TabsWrapper>
|
||||
<EntityTabs tabs={visibleTabs} selectedTab={routedTab} />
|
||||
</TabsWrapper>
|
||||
)}
|
||||
<TabContent>
|
||||
{routedTab && (
|
||||
<routedTab.component
|
||||
properties={routedTab.properties}
|
||||
contextType={TabContextType.PROFILE}
|
||||
renderType={TabRenderType.DEFAULT}
|
||||
/>
|
||||
)}
|
||||
</TabContent>
|
||||
<EntityTabs tabs={visibleTabs} selectedTab={routedTab} />
|
||||
</BodyContent>
|
||||
</Body>
|
||||
</HeaderAndTabsFlex>
|
||||
|
||||
@ -1,82 +1,31 @@
|
||||
import { Tabs } from 'antd';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Tabs } from '@components';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import styled from 'styled-components/macro';
|
||||
|
||||
import { Tab } from '@components/components/Tabs/Tabs';
|
||||
|
||||
import { useBaseEntity, useEntityData, useRouteToTab } from '@app/entity/shared/EntityContext';
|
||||
import { EntityTab } from '@app/entityV2/shared/types';
|
||||
import { EntityTab, TabContextType, TabRenderType } from '@app/entityV2/shared/types';
|
||||
import TabFullsizedContext from '@app/shared/TabFullsizedContext';
|
||||
|
||||
type Props = {
|
||||
tabs: EntityTab[];
|
||||
selectedTab?: EntityTab;
|
||||
};
|
||||
|
||||
const Header = styled.div`
|
||||
const TabContent = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const UnborderedTabs = styled(Tabs)`
|
||||
width: 100%;
|
||||
padding: 12px 14px 10px 12px;
|
||||
|
||||
&&& .ant-tabs-nav {
|
||||
margin-bottom: 0;
|
||||
|
||||
&::before {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
&&& .ant-tabs-nav-wrap {
|
||||
background-color: #f6f7fa;
|
||||
border-radius: 4px;
|
||||
gap: 3px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
&&& .ant-tabs-tab {
|
||||
padding: 10px 16px;
|
||||
margin: 0;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
&&& .ant-tabs-tab-active {
|
||||
background-color: ${(p) => p.theme.styles['primary-color']};
|
||||
}
|
||||
|
||||
&&& .ant-tabs-tab-active .ant-tabs-tab-btn {
|
||||
color: #ffffff;
|
||||
|
||||
&:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
&&& .ant-tabs-ink-bar {
|
||||
height: 0px;
|
||||
}
|
||||
|
||||
&&& .ant-tabs-content-holder {
|
||||
display: none;
|
||||
}
|
||||
|
||||
background-color: #ffffff;
|
||||
`;
|
||||
|
||||
const Tab = styled(Tabs.TabPane)`
|
||||
font-size: 10px;
|
||||
line-height: normal;
|
||||
font-weight: 400;
|
||||
`;
|
||||
|
||||
const tabIconStyle = { fontSize: 14, marginRight: 6 };
|
||||
|
||||
export const EntityTabs = <T,>({ tabs, selectedTab }: Props) => {
|
||||
const { entityData, loading } = useEntityData();
|
||||
const routeToTab = useRouteToTab();
|
||||
const baseEntity = useBaseEntity<T>();
|
||||
const { isTabFullsize } = useContext(TabFullsizedContext);
|
||||
|
||||
const enabledTabs = tabs.filter((tab) => tab.display?.enabled(entityData, baseEntity));
|
||||
|
||||
@ -86,42 +35,30 @@ export const EntityTabs = <T,>({ tabs, selectedTab }: Props) => {
|
||||
}
|
||||
}, [loading, enabledTabs, selectedTab, routeToTab]);
|
||||
|
||||
const finalTabs: Tab[] = tabs.map((t) => ({
|
||||
key: t.name,
|
||||
name: t.name,
|
||||
component: (
|
||||
<TabContent>
|
||||
<t.component
|
||||
properties={t.properties}
|
||||
contextType={TabContextType.PROFILE}
|
||||
renderType={TabRenderType.DEFAULT}
|
||||
/>
|
||||
</TabContent>
|
||||
),
|
||||
disabled: !t.display?.enabled(entityData, baseEntity),
|
||||
dataTestId: `${t.name}-entity-tab-header`,
|
||||
count: t.getCount?.(entityData, baseEntity, loading),
|
||||
}));
|
||||
|
||||
return (
|
||||
<UnborderedTabs
|
||||
animated={false}
|
||||
activeKey={selectedTab?.name || ''}
|
||||
size="large"
|
||||
onTabClick={(tab: string) => routeToTab({ tabName: tab })}
|
||||
>
|
||||
{tabs.map((tab) => {
|
||||
const TabIcon = tab.icon;
|
||||
const tabName = (tab.getDynamicName && tab.getDynamicName(entityData, baseEntity, loading)) || tab.name;
|
||||
if (!tab.display?.enabled(entityData, baseEntity)) {
|
||||
return (
|
||||
<Tab
|
||||
tab={
|
||||
<span data-testid={`${tab.name}-entity-tab-header`}>
|
||||
{TabIcon && <TabIcon style={tabIconStyle} />}
|
||||
{tab.name}
|
||||
</span>
|
||||
}
|
||||
key={tab.name}
|
||||
disabled
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Tab
|
||||
tab={
|
||||
<Header data-testid={`${tab.name}-entity-tab-header`}>
|
||||
{TabIcon && <TabIcon style={tabIconStyle} />}
|
||||
{tabName}
|
||||
</Header>
|
||||
}
|
||||
key={tab.name}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</UnborderedTabs>
|
||||
<Tabs
|
||||
onChange={(t) => routeToTab({ tabName: t })}
|
||||
selectedTab={selectedTab?.name}
|
||||
tabs={finalTabs}
|
||||
hideTabsHeader={isTabFullsize}
|
||||
addPaddingLeft
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -4,6 +4,7 @@ import { useHistory, useLocation } from 'react-router';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { useEntityData } from '@app/entity/shared/EntityContext';
|
||||
import { QUALITY_TAB_NAME } from '@app/entityV2/dataset/constants';
|
||||
import { AcrylAssertionList } from '@app/entityV2/shared/tabs/Dataset/Validations/AssertionList/AcrylAssertionList';
|
||||
import { AcrylAssertionSummaryTab } from '@app/entityV2/shared/tabs/Dataset/Validations/AssertionList/Summary/AcrylAssertionSummaryTab';
|
||||
import { DataContractTab } from '@app/entityV2/shared/tabs/Dataset/Validations/contract/DataContractTab';
|
||||
@ -67,7 +68,7 @@ export const AcrylValidationsTab = () => {
|
||||
|
||||
// If no tab was selected, select a default tab.
|
||||
useEffect(() => {
|
||||
if (!selectedTab) {
|
||||
if (!selectedTab && basePath.endsWith(QUALITY_TAB_NAME)) {
|
||||
// Route to the default tab.
|
||||
history.replace(`${basePath}/${DEFAULT_TAB}?${SEPARATE_SIBLINGS_URL_PARAM}=${isHideSiblingMode}`);
|
||||
}
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useGetEntityWithSchema } from '@app/entityV2/shared/tabs/Dataset/Schema/useGetEntitySchema';
|
||||
import TabNameWithCount from '@app/entityV2/shared/tabs/Entity/TabNameWithCount';
|
||||
|
||||
const ColumnTabNameHeader = () => {
|
||||
const { entityWithSchema, loading } = useGetEntityWithSchema();
|
||||
const fieldsCount = entityWithSchema?.schemaMetadata?.fields?.length || 0;
|
||||
|
||||
return <TabNameWithCount name="Columns" count={fieldsCount} loading={loading} />;
|
||||
};
|
||||
|
||||
export default ColumnTabNameHeader;
|
||||
@ -1,30 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Pill } from '@src/alchemy-components';
|
||||
import { formatNumber } from '@src/app/shared/formatNumber';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const TabName = styled.div`
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
name: string;
|
||||
count: number;
|
||||
loading: boolean;
|
||||
};
|
||||
|
||||
const TabNameWithCount = ({ name, count = 0, loading }: Props) => {
|
||||
return (
|
||||
<Container>
|
||||
<TabName>{name}</TabName>
|
||||
{!loading && <Pill label={formatNumber(count)} size="sm" />}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
export default TabNameWithCount;
|
||||
@ -40,7 +40,7 @@ const StyledList = styled(List)`
|
||||
margin-left: -20px;
|
||||
border-bottom: none;
|
||||
padding-bottom: 0px;
|
||||
padding-top: 15px;
|
||||
padding-top: 0px;
|
||||
}
|
||||
` as typeof List;
|
||||
|
||||
@ -106,7 +106,6 @@ export const EntityList = ({
|
||||
<>
|
||||
<ScrollWrapper>
|
||||
<StyledList
|
||||
bordered
|
||||
dataSource={entities}
|
||||
header={title || `${entities.length || 0} ${entityRegistry.getCollectionName(type)}`}
|
||||
renderItem={(item) => (
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
export enum TabRenderType {
|
||||
/**
|
||||
* A default, full screen tab.
|
||||
@ -63,7 +61,7 @@ export type EntityTab = {
|
||||
};
|
||||
properties?: any;
|
||||
id?: string;
|
||||
getDynamicName?: (GenericEntityProperties, T, loading: boolean) => ReactElement;
|
||||
getCount?: (GenericEntityProperties, T, loading: boolean) => number | undefined;
|
||||
supportsFullsize?: boolean; // As per TabFullsizedContext
|
||||
};
|
||||
|
||||
|
||||
@ -82,7 +82,7 @@ describe("glossaryTerm", () => {
|
||||
it("can apply filters on related entities", () => {
|
||||
cy.clickOptionWithText("CypressNode");
|
||||
cy.clickOptionWithText("GlossaryNewTerm");
|
||||
cy.clickOptionWithSpecificClass(".anticon.anticon-appstore", 0);
|
||||
cy.clickTextOptionWithClass(".ant-tabs-tab", "Related Assets");
|
||||
elementVisibility();
|
||||
cy.clickOptionWithSpecificClass(".anticon-filter", 0);
|
||||
cy.waitTextVisible("Filter");
|
||||
@ -102,7 +102,7 @@ describe("glossaryTerm", () => {
|
||||
it("can search related entities by a specific tag using advanced search", () => {
|
||||
cy.clickOptionWithText("CypressNode");
|
||||
cy.clickOptionWithText("GlossaryNewTerm");
|
||||
cy.clickOptionWithSpecificClass(".anticon.anticon-appstore", 0);
|
||||
cy.clickTextOptionWithClass(".ant-tabs-tab", "Related Assets");
|
||||
elementVisibility();
|
||||
applyAdvancedSearchFilter("Tag", "Cypress");
|
||||
elementVisibility();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user