mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-07-18 22:53:09 +00:00
feat(ui): breadcrumb style updates (#10974)
* feat(ui): breadcrumb style updates entity title styling * fix localisation * update explore card & summary panel breadcrumbs * fix breadcrumbs for container, glossary & tags * update breadcrumb styling at all the places * fix service type missing from version components * fix unit tests * fix breadcrumb styling fix icons related changes fix link related issue * fixed assets related feedbacks * add external link for assets * fix cypress * fix breadcrumbs on the glossary * fix glossary breadcrumb issue * update databaseSchema page layout * fix code smells * fix code smells * fix unit tests failing * update tag icon and fix lineage cypress * fix restore entity * fix restore entity spec * fix test id issue
This commit is contained in:
parent
91b04ee9a3
commit
c89bcb51b4
@ -323,7 +323,7 @@ export const deleteCreatedService = (
|
|||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.get(`[data-testid="inactive-link"]`)
|
cy.get(`[data-testid="entity-header-name"]`)
|
||||||
.should('exist')
|
.should('exist')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
|
@ -79,11 +79,12 @@ describe('Restore entity functionality should work properly', () => {
|
|||||||
cy.get('[data-testid="show-deleted"]').should('exist').click();
|
cy.get('[data-testid="show-deleted"]').should('exist').click();
|
||||||
verifyResponseStatusCode('@showDeletedTables', 200);
|
verifyResponseStatusCode('@showDeletedTables', 200);
|
||||||
|
|
||||||
cy.get('[data-testid="sample_data-raw_product_catalog"]')
|
cy.get('[data-testid="entity-header-display-name"]')
|
||||||
|
.contains('raw_product_catalog')
|
||||||
.should('exist')
|
.should('exist')
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.get('[data-testid="inactive-link"]')
|
cy.get('[data-testid="entity-header-display-name"]')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.contains(ENTITY_TABLE.displayName);
|
.contains(ENTITY_TABLE.displayName);
|
||||||
|
|
||||||
@ -96,18 +97,20 @@ describe('Restore entity functionality should work properly', () => {
|
|||||||
cy.get('[data-testid="show-deleted"]').should('exist').click();
|
cy.get('[data-testid="show-deleted"]').should('exist').click();
|
||||||
verifyResponseStatusCode('@showDeletedTables', 200);
|
verifyResponseStatusCode('@showDeletedTables', 200);
|
||||||
|
|
||||||
cy.get('[data-testid="sample_data-raw_product_catalog"]')
|
cy.get('[data-testid="entity-header-display-name"]')
|
||||||
|
.contains('raw_product_catalog')
|
||||||
.should('exist')
|
.should('exist')
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.get('[data-testid="inactive-link"]')
|
cy.get('[data-testid="entity-header-display-name"]')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.contains(ENTITY_TABLE.displayName)
|
.contains(ENTITY_TABLE.displayName)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.get('[data-testid="deleted-badge"]').should('exist');
|
cy.get('[data-testid="deleted-badge"]').should('exist');
|
||||||
|
|
||||||
cy.get('[data-testid="breadcrumb-link"]')
|
cy.get('[data-testid="breadcrumb"]')
|
||||||
|
.scrollIntoView()
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.contains(ENTITY_TABLE.schemaName)
|
.contains(ENTITY_TABLE.schemaName)
|
||||||
.click();
|
.click();
|
||||||
@ -142,11 +145,12 @@ describe('Restore entity functionality should work properly', () => {
|
|||||||
cy.get('[data-testid="show-deleted"]').should('exist').click();
|
cy.get('[data-testid="show-deleted"]').should('exist').click();
|
||||||
verifyResponseStatusCode('@showDeletedTables', 200);
|
verifyResponseStatusCode('@showDeletedTables', 200);
|
||||||
|
|
||||||
cy.get('[data-testid="sample_data-raw_product_catalog"]')
|
cy.get('[data-testid="entity-header-display-name"]')
|
||||||
|
.contains('raw_product_catalog')
|
||||||
.should('exist')
|
.should('exist')
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.get('[data-testid="inactive-link"]')
|
cy.get('[data-testid="entity-header-display-name"]')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.contains(ENTITY_TABLE.displayName);
|
.contains(ENTITY_TABLE.displayName);
|
||||||
|
|
||||||
|
@ -582,7 +582,9 @@ describe('Data Quality and Profiler should work properly', () => {
|
|||||||
const { term, entity, serviceName, testCaseName } =
|
const { term, entity, serviceName, testCaseName } =
|
||||||
DATA_QUALITY_SAMPLE_DATA_TABLE;
|
DATA_QUALITY_SAMPLE_DATA_TABLE;
|
||||||
visitEntityDetailsPage(term, serviceName, entity);
|
visitEntityDetailsPage(term, serviceName, entity);
|
||||||
cy.get('[data-testid="inactive-link"]').should('be.visible').contains(term);
|
cy.get('[data-testid="entity-header-name"]')
|
||||||
|
.should('be.visible')
|
||||||
|
.contains(term);
|
||||||
cy.get('[data-testid="Profiler & Data Quality"]')
|
cy.get('[data-testid="Profiler & Data Quality"]')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.click();
|
.click();
|
||||||
@ -633,7 +635,9 @@ describe('Data Quality and Profiler should work properly', () => {
|
|||||||
);
|
);
|
||||||
visitEntityDetailsPage(term, serviceName, entity);
|
visitEntityDetailsPage(term, serviceName, entity);
|
||||||
verifyResponseStatusCode('@waitForPageLoad', 200);
|
verifyResponseStatusCode('@waitForPageLoad', 200);
|
||||||
cy.get('[data-testid="inactive-link"]').should('be.visible').contains(term);
|
cy.get('[data-testid="entity-header-name"]')
|
||||||
|
.should('be.visible')
|
||||||
|
.contains(term);
|
||||||
cy.get('[data-testid="Profiler & Data Quality"]')
|
cy.get('[data-testid="Profiler & Data Quality"]')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.click();
|
.click();
|
||||||
|
@ -704,7 +704,7 @@ describe('Glossary page should work properly', () => {
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
cy.get(`[data-testid="${entity.serviceName}-${entity.term}"]`)
|
cy.get('[data-testid="entity-header-display-name"]')
|
||||||
.contains(entity.term)
|
.contains(entity.term)
|
||||||
.should('be.visible');
|
.should('be.visible');
|
||||||
});
|
});
|
||||||
@ -722,7 +722,8 @@ describe('Glossary page should work properly', () => {
|
|||||||
verifyResponseStatusCode('@assetTab', 200);
|
verifyResponseStatusCode('@assetTab', 200);
|
||||||
|
|
||||||
interceptURL('GET', '/api/v1/feed*', 'entityDetails');
|
interceptURL('GET', '/api/v1/feed*', 'entityDetails');
|
||||||
cy.get(`[data-testid="${entity.serviceName}-${entity.term}"]`)
|
|
||||||
|
cy.get('[data-testid="entity-header-display-name"]')
|
||||||
.contains(entity.term)
|
.contains(entity.term)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.click();
|
.click();
|
||||||
|
@ -88,7 +88,7 @@ describe('MyData page should work', () => {
|
|||||||
entity.entityObj.serviceName,
|
entity.entityObj.serviceName,
|
||||||
entity.entityObj.entity
|
entity.entityObj.entity
|
||||||
);
|
);
|
||||||
cy.get('[data-testid="inactive-link"]')
|
cy.get('[data-testid="entity-header-display-name"]')
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.then((newText) => {
|
.then((newText) => {
|
||||||
expect(newText).equal(text);
|
expect(newText).equal(text);
|
||||||
@ -98,7 +98,7 @@ describe('MyData page should work', () => {
|
|||||||
.contains(text)
|
.contains(text)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.click();
|
.click();
|
||||||
cy.get('[data-testid="inactive-link"]')
|
cy.get('[data-testid="entity-header-display-name"]')
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.then((newText) => {
|
.then((newText) => {
|
||||||
expect(newText).equal(text);
|
expect(newText).equal(text);
|
||||||
|
@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none"><g fill="#37352F" stroke="#37352F" stroke-width=".1" clip-path="url(#a)"><path d="M13.461 8.077a.461.461 0 0 0-.461.461v4.154a1.385 1.385 0 0 1-1.385 1.385H3.308a1.385 1.385 0 0 1-1.385-1.385V4.385A1.385 1.385 0 0 1 3.308 3h4.154a.462.462 0 1 0 0-.923H3.308A2.308 2.308 0 0 0 1 4.385v8.307A2.308 2.308 0 0 0 3.308 15h8.307a2.308 2.308 0 0 0 2.308-2.308V8.538a.461.461 0 0 0-.462-.461Z"/><path d="M10.385 2.154h2.648l-6.52 6.513a.577.577 0 0 0 .188.946.577.577 0 0 0 .632-.126l6.513-6.514v2.642a.577.577 0 0 0 1.154 0V1.577a.578.578 0 0 0-.355-.534.577.577 0 0 0-.222-.043h-4.038a.577.577 0 1 0 0 1.154Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none"><g fill="#37352F" stroke="#37352F" stroke-width=".1" clip-path="url(#a)"><path d="M13.461 8.077a.461.461 0 0 0-.461.461v4.154a1.385 1.385 0 0 1-1.385 1.385H3.308a1.385 1.385 0 0 1-1.385-1.385V4.385A1.385 1.385 0 0 1 3.308 3h4.154a.462.462 0 1 0 0-.923H3.308A2.308 2.308 0 0 0 1 4.385v8.307A2.308 2.308 0 0 0 3.308 15h8.307a2.308 2.308 0 0 0 2.308-2.308V8.538a.461.461 0 0 0-.462-.461Z"/><path d="M10.385 2.154h2.648l-6.52 6.513a.577.577 0 0 0 .188.946.577.577 0 0 0 .632-.126l6.513-6.514v2.642a.577.577 0 0 0 1.154 0V1.577a.578.578 0 0 0-.355-.534.577.577 0 0 0-.222-.043h-4.038a.577.577 0 1 0 0 1.154Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
Before Width: | Height: | Size: 768 B After Width: | Height: | Size: 765 B |
@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none"><path fill="#7147E8" d="M6.856 12c.376 0 .73-.147.995-.413l3.739-3.744a1.409 1.409 0 0 0 0-1.987L6.55.807A2.731 2.731 0 0 0 4.604 0H1.406C.631 0 0 .63 0 1.406v3.188c0 .735.286 1.425.806 1.945l5.056 5.05c.266.265.619.411.994.411ZM4.604.937a1.8 1.8 0 0 1 1.282.532l5.04 5.05a.47.47 0 0 1 0 .662l-3.739 3.744a.466.466 0 0 1-.33.137h-.001a.466.466 0 0 1-.331-.136l-5.056-5.05a1.8 1.8 0 0 1-.531-1.282V1.406a.47.47 0 0 1 .468-.468h3.198Zm-1.205 3.82c.775 0 1.406-.63 1.406-1.405 0-.776-.63-1.407-1.406-1.407a1.408 1.408 0 0 0 0 2.813Zm0-1.874a.47.47 0 1 1-.001.938.47.47 0 0 1 0-.938Z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" fill="none"><path fill="#7147E8" d="M6.856 12c.376 0 .73-.147.995-.413l3.739-3.744a1.409 1.409 0 0 0 0-1.987L6.55.807A2.731 2.731 0 0 0 4.604 0H1.406C.631 0 0 .63 0 1.406v3.188c0 .735.286 1.425.806 1.945l5.056 5.05c.266.265.619.411.994.411ZM4.604.937a1.8 1.8 0 0 1 1.282.532l5.04 5.05a.47.47 0 0 1 0 .662l-3.739 3.744a.466.466 0 0 1-.33.137h-.001a.466.466 0 0 1-.331-.136l-5.056-5.05a1.8 1.8 0 0 1-.531-1.282V1.406a.47.47 0 0 1 .468-.468h3.198Zm-1.205 3.82c.775 0 1.406-.63 1.406-1.405 0-.776-.63-1.407-1.406-1.407a1.408 1.408 0 0 0 0 2.813Zm0-1.874a.47.47 0 1 1-.001.938.47.47 0 0 1 0-.938Z"/></svg>
|
Before Width: | Height: | Size: 663 B After Width: | Height: | Size: 660 B |
@ -56,14 +56,6 @@ jest.mock('./ActivityThreadList', () => {
|
|||||||
return jest.fn().mockReturnValue(<p>ActivityThreadList</p>);
|
return jest.fn().mockReturnValue(<p>ActivityThreadList</p>);
|
||||||
});
|
});
|
||||||
|
|
||||||
const mockObserve = jest.fn();
|
|
||||||
const mockunObserve = jest.fn();
|
|
||||||
|
|
||||||
window.IntersectionObserver = jest.fn().mockImplementation(() => ({
|
|
||||||
observe: mockObserve,
|
|
||||||
unobserve: mockunObserve,
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('Test ActivityThreadPanel Component', () => {
|
describe('Test ActivityThreadPanel Component', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
@ -100,7 +92,5 @@ describe('Test ActivityThreadPanel Component', () => {
|
|||||||
const obServerElement = await screen.findByTestId('observer-element');
|
const obServerElement = await screen.findByTestId('observer-element');
|
||||||
|
|
||||||
expect(obServerElement).toBeInTheDocument();
|
expect(obServerElement).toBeInTheDocument();
|
||||||
|
|
||||||
expect(mockObserve).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -51,14 +51,6 @@ jest.mock('./ActivityThreadList', () => {
|
|||||||
return jest.fn().mockReturnValue(<p>ActivityThreadList</p>);
|
return jest.fn().mockReturnValue(<p>ActivityThreadList</p>);
|
||||||
});
|
});
|
||||||
|
|
||||||
const mockObserve = jest.fn();
|
|
||||||
const mockunObserve = jest.fn();
|
|
||||||
|
|
||||||
window.IntersectionObserver = jest.fn().mockImplementation(() => ({
|
|
||||||
observe: mockObserve,
|
|
||||||
unobserve: mockunObserve,
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('Test ActivityThreadPanelBodyBody Component', () => {
|
describe('Test ActivityThreadPanelBodyBody Component', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
@ -89,7 +81,5 @@ describe('Test ActivityThreadPanelBodyBody Component', () => {
|
|||||||
const obServerElement = await findByTestId(container, 'observer-element');
|
const obServerElement = await findByTestId(container, 'observer-element');
|
||||||
|
|
||||||
expect(obServerElement).toBeInTheDocument();
|
expect(obServerElement).toBeInTheDocument();
|
||||||
|
|
||||||
expect(mockObserve).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -55,6 +55,7 @@ export const AssetSelectionModal = ({
|
|||||||
SearchIndex.TABLE
|
SearchIndex.TABLE
|
||||||
);
|
);
|
||||||
const [pageNumber, setPageNumber] = useState(1);
|
const [pageNumber, setPageNumber] = useState(1);
|
||||||
|
const [totalCount, setTotalCount] = useState(0);
|
||||||
|
|
||||||
const fetchEntities = useCallback(
|
const fetchEntities = useCallback(
|
||||||
async ({ searchText = '', page = 1, index = activeFilter }) => {
|
async ({ searchText = '', page = 1, index = activeFilter }) => {
|
||||||
@ -68,7 +69,7 @@ export const AssetSelectionModal = ({
|
|||||||
queryFilter: getQueryFilterToExcludeTerm(glossaryFQN),
|
queryFilter: getQueryFilterToExcludeTerm(glossaryFQN),
|
||||||
});
|
});
|
||||||
const hits = res.hits.hits as SearchedDataProps['data'];
|
const hits = res.hits.hits as SearchedDataProps['data'];
|
||||||
|
setTotalCount(res.hits.total.value ?? 0);
|
||||||
setItems(page === 1 ? hits : (prevItems) => [...prevItems, ...hits]);
|
setItems(page === 1 ? hits : (prevItems) => [...prevItems, ...hits]);
|
||||||
setPageNumber(page);
|
setPageNumber(page);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -174,7 +175,10 @@ export const AssetSelectionModal = ({
|
|||||||
|
|
||||||
const onScroll: UIEventHandler<HTMLElement> = useCallback(
|
const onScroll: UIEventHandler<HTMLElement> = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
if (e.currentTarget.scrollHeight - e.currentTarget.scrollTop === 500) {
|
if (
|
||||||
|
e.currentTarget.scrollHeight - e.currentTarget.scrollTop === 500 &&
|
||||||
|
items.length < totalCount
|
||||||
|
) {
|
||||||
!isLoading &&
|
!isLoading &&
|
||||||
fetchEntities({
|
fetchEntities({
|
||||||
searchText: search,
|
searchText: search,
|
||||||
@ -183,7 +187,7 @@ export const AssetSelectionModal = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[activeFilter, search]
|
[activeFilter, search, totalCount, items]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -202,7 +206,8 @@ export const AssetSelectionModal = ({
|
|||||||
open={open}
|
open={open}
|
||||||
style={{ top: 40 }}
|
style={{ top: 40 }}
|
||||||
title={t('label.add-entity', { entity: t('label.asset-plural') })}
|
title={t('label.add-entity', { entity: t('label.asset-plural') })}
|
||||||
width={750}>
|
width={750}
|
||||||
|
onCancel={onCancel}>
|
||||||
<Space className="w-full h-full" direction="vertical" size={16}>
|
<Space className="w-full h-full" direction="vertical" size={16}>
|
||||||
<Searchbar
|
<Searchbar
|
||||||
removeMargin
|
removeMargin
|
||||||
@ -234,7 +239,7 @@ export const AssetSelectionModal = ({
|
|||||||
height={500}
|
height={500}
|
||||||
itemKey="id"
|
itemKey="id"
|
||||||
onScroll={onScroll}>
|
onScroll={onScroll}>
|
||||||
{({ _index: index, _source: item }) => (
|
{({ _source: item }) => (
|
||||||
<TableDataCardV2
|
<TableDataCardV2
|
||||||
openEntityInNewPage
|
openEntityInNewPage
|
||||||
showCheckboxes
|
showCheckboxes
|
||||||
@ -243,8 +248,7 @@ export const AssetSelectionModal = ({
|
|||||||
handleSummaryPanelDisplay={handleCardClick}
|
handleSummaryPanelDisplay={handleCardClick}
|
||||||
id={`tabledatacard-${item.id}`}
|
id={`tabledatacard-${item.id}`}
|
||||||
key={item.id}
|
key={item.id}
|
||||||
searchIndex={index}
|
source={{ ...item, tags: [] }}
|
||||||
source={item}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</VirtualList>
|
</VirtualList>
|
||||||
|
@ -16,3 +16,20 @@
|
|||||||
.asset-selection-model-card.table-data-card-container:hover {
|
.asset-selection-model-card.table-data-card-container:hover {
|
||||||
box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.13);
|
box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.13);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.asset-selection-model-card {
|
||||||
|
// CheckBox
|
||||||
|
.ant-checkbox-inner {
|
||||||
|
border-width: 2px;
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
border-color: #7147e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-checkbox-inner::after {
|
||||||
|
top: 48%;
|
||||||
|
left: 18.5%;
|
||||||
|
width: 6.25px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -399,6 +399,7 @@ const ContainerVersion: React.FC<ContainerVersionProp> = ({
|
|||||||
entityName={currentVersionData.name ?? ''}
|
entityName={currentVersionData.name ?? ''}
|
||||||
extraInfo={getExtraInfo()}
|
extraInfo={getExtraInfo()}
|
||||||
followersList={[]}
|
followersList={[]}
|
||||||
|
serviceType={currentVersionData.serviceType ?? ''}
|
||||||
tags={getTags()}
|
tags={getTags()}
|
||||||
tier={undefined}
|
tier={undefined}
|
||||||
titleLinks={breadCrumbList}
|
titleLinks={breadCrumbList}
|
||||||
|
@ -662,6 +662,7 @@ const DashboardDetails = ({
|
|||||||
? onRemoveTier
|
? onRemoveTier
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
serviceType={dashboardDetails.serviceType ?? ''}
|
||||||
tags={dashboardTags}
|
tags={dashboardTags}
|
||||||
tagsHandler={onTagUpdate}
|
tagsHandler={onTagUpdate}
|
||||||
tier={tier}
|
tier={tier}
|
||||||
|
@ -112,14 +112,6 @@ const DashboardDetailsProps = {
|
|||||||
onExtensionUpdate: jest.fn(),
|
onExtensionUpdate: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockObserve = jest.fn();
|
|
||||||
const mockunObserve = jest.fn();
|
|
||||||
|
|
||||||
window.IntersectionObserver = jest.fn().mockImplementation(() => ({
|
|
||||||
observe: mockObserve,
|
|
||||||
unobserve: mockunObserve,
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('../common/description/Description', () => {
|
jest.mock('../common/description/Description', () => {
|
||||||
return jest.fn().mockReturnValue(<p>Description Component</p>);
|
return jest.fn().mockReturnValue(<p>Description Component</p>);
|
||||||
});
|
});
|
||||||
@ -285,8 +277,6 @@ describe('Test DashboardDetails component', () => {
|
|||||||
const obServerElement = await findByTestId(container, 'observer-element');
|
const obServerElement = await findByTestId(container, 'observer-element');
|
||||||
|
|
||||||
expect(obServerElement).toBeInTheDocument();
|
expect(obServerElement).toBeInTheDocument();
|
||||||
|
|
||||||
expect(mockObserve).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Check if tags and glossary-terms are present', async () => {
|
it('Check if tags and glossary-terms are present', async () => {
|
||||||
|
@ -283,6 +283,7 @@ const DashboardVersion: FC<DashboardVersionProp> = ({
|
|||||||
}
|
}
|
||||||
extraInfo={getExtraInfo()}
|
extraInfo={getExtraInfo()}
|
||||||
followersList={[]}
|
followersList={[]}
|
||||||
|
serviceType={currentVersionData.serviceType ?? ''}
|
||||||
tags={getTags()}
|
tags={getTags()}
|
||||||
tier={{} as TagLabel}
|
tier={{} as TagLabel}
|
||||||
titleLinks={slashedDashboardName}
|
titleLinks={slashedDashboardName}
|
||||||
|
@ -268,6 +268,7 @@ const DataModelVersion: FC<DataModelVersionProp> = ({
|
|||||||
}
|
}
|
||||||
extraInfo={getExtraInfo()}
|
extraInfo={getExtraInfo()}
|
||||||
followersList={[]}
|
followersList={[]}
|
||||||
|
serviceType={currentVersionData.serviceType ?? ''}
|
||||||
tags={getTags()}
|
tags={getTags()}
|
||||||
tier={{} as TagLabel}
|
tier={{} as TagLabel}
|
||||||
titleLinks={slashedDataModelName}
|
titleLinks={slashedDataModelName}
|
||||||
|
@ -44,7 +44,6 @@ import {
|
|||||||
} from 'utils/CommonUtils';
|
} from 'utils/CommonUtils';
|
||||||
import { getEntityName } from 'utils/EntityUtils';
|
import { getEntityName } from 'utils/EntityUtils';
|
||||||
import { getEntityFieldThreadCounts } from 'utils/FeedUtils';
|
import { getEntityFieldThreadCounts } from 'utils/FeedUtils';
|
||||||
import { serviceTypeLogo } from 'utils/ServiceUtils';
|
|
||||||
import { getTagsWithoutTier, getTierTags } from 'utils/TableUtils';
|
import { getTagsWithoutTier, getTierTags } from 'utils/TableUtils';
|
||||||
import { DataModelDetailsProps } from './DataModelDetails.interface';
|
import { DataModelDetailsProps } from './DataModelDetails.interface';
|
||||||
import ModelTab from './ModelTab/ModelTab.component';
|
import ModelTab from './ModelTab/ModelTab.component';
|
||||||
@ -119,6 +118,7 @@ const DataModelDetails = ({
|
|||||||
followers,
|
followers,
|
||||||
dataModelType,
|
dataModelType,
|
||||||
isUserFollowing,
|
isUserFollowing,
|
||||||
|
serviceType,
|
||||||
} = useMemo(() => {
|
} = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
deleted: dataModelData?.deleted,
|
deleted: dataModelData?.deleted,
|
||||||
@ -134,11 +134,11 @@ const DataModelDetails = ({
|
|||||||
),
|
),
|
||||||
followers: dataModelData?.followers ?? [],
|
followers: dataModelData?.followers ?? [],
|
||||||
dataModelType: dataModelData?.dataModelType,
|
dataModelType: dataModelData?.dataModelType,
|
||||||
|
serviceType: dataModelData?.serviceType,
|
||||||
};
|
};
|
||||||
}, [dataModelData]);
|
}, [dataModelData]);
|
||||||
|
|
||||||
const breadcrumbTitles = useMemo(() => {
|
const breadcrumbTitles = useMemo(() => {
|
||||||
const serviceType = dataModelData?.serviceType;
|
|
||||||
const service = dataModelData?.service;
|
const service = dataModelData?.service;
|
||||||
const serviceName = service?.name;
|
const serviceName = service?.name;
|
||||||
|
|
||||||
@ -151,12 +151,6 @@ const DataModelDetails = ({
|
|||||||
ServiceCategory.DASHBOARD_SERVICES
|
ServiceCategory.DASHBOARD_SERVICES
|
||||||
)
|
)
|
||||||
: '',
|
: '',
|
||||||
imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: entityName,
|
|
||||||
url: '',
|
|
||||||
activeTitle: true,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}, [dataModelData, dashboardDataModelFQN, entityName]);
|
}, [dataModelData, dashboardDataModelFQN, entityName]);
|
||||||
@ -247,6 +241,7 @@ const DataModelDetails = ({
|
|||||||
isFollowing={isUserFollowing}
|
isFollowing={isUserFollowing}
|
||||||
isTagEditable={hasEditTagsPermission}
|
isTagEditable={hasEditTagsPermission}
|
||||||
removeTier={hasEditTierPermission ? handleRemoveTier : undefined}
|
removeTier={hasEditTierPermission ? handleRemoveTier : undefined}
|
||||||
|
serviceType={serviceType ?? ''}
|
||||||
tags={tags}
|
tags={tags}
|
||||||
tagsHandler={handleUpdateTags}
|
tagsHandler={handleUpdateTags}
|
||||||
tier={tier}
|
tier={tier}
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
import { Card, Col, Row, Skeleton, Space, Typography } from 'antd';
|
import { Card, Col, Row, Skeleton, Space, Typography } from 'antd';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
// css
|
||||||
|
import QueryCount from 'components/common/QueryCount/QueryCount.component';
|
||||||
|
import PageLayoutV1 from 'components/containers/PageLayoutV1';
|
||||||
import { isEqual, isNil, isUndefined } from 'lodash';
|
import { isEqual, isNil, isUndefined } from 'lodash';
|
||||||
import { EntityTags, ExtraInfo } from 'Models';
|
import { EntityTags, ExtraInfo } from 'Models';
|
||||||
import React, {
|
import React, {
|
||||||
@ -82,8 +85,6 @@ import TableProfilerGraph from '../TableProfiler/TableProfilerGraph.component';
|
|||||||
import TableProfilerV1 from '../TableProfiler/TableProfilerV1';
|
import TableProfilerV1 from '../TableProfiler/TableProfilerV1';
|
||||||
import TableQueries from '../TableQueries/TableQueries';
|
import TableQueries from '../TableQueries/TableQueries';
|
||||||
import { DatasetDetailsProps } from './DatasetDetails.interface';
|
import { DatasetDetailsProps } from './DatasetDetails.interface';
|
||||||
// css
|
|
||||||
import QueryCount from 'components/common/QueryCount/QueryCount.component';
|
|
||||||
import './datasetDetails.style.less';
|
import './datasetDetails.style.less';
|
||||||
|
|
||||||
const DatasetDetails: React.FC<DatasetDetailsProps> = ({
|
const DatasetDetails: React.FC<DatasetDetailsProps> = ({
|
||||||
@ -606,7 +607,10 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
|
|||||||
<Loader />
|
<Loader />
|
||||||
) : (
|
) : (
|
||||||
<PageContainerV1>
|
<PageContainerV1>
|
||||||
<div className="entity-details-container">
|
<PageLayoutV1
|
||||||
|
pageTitle={t('label.entity-detail-plural', {
|
||||||
|
entity: getEntityName(tableDetails),
|
||||||
|
})}>
|
||||||
<EntityPageInfo
|
<EntityPageInfo
|
||||||
canDelete={tablePermissions.Delete}
|
canDelete={tablePermissions.Delete}
|
||||||
currentOwner={tableDetails.owner}
|
currentOwner={tableDetails.owner}
|
||||||
@ -634,6 +638,7 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
|
|||||||
? onRemoveTier
|
? onRemoveTier
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
serviceType={tableDetails.serviceType ?? ''}
|
||||||
tags={tableTags}
|
tags={tableTags}
|
||||||
tagsHandler={onTagUpdate}
|
tagsHandler={onTagUpdate}
|
||||||
tier={tier}
|
tier={tier}
|
||||||
@ -654,7 +659,7 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
|
|||||||
onThreadLinkSelect={onThreadLinkSelect}
|
onThreadLinkSelect={onThreadLinkSelect}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="tw-mt-4 tw-flex tw-flex-col tw-flex-grow">
|
<div className="m-t-md h-inherit">
|
||||||
<TabsPane
|
<TabsPane
|
||||||
activeTab={activeTab}
|
activeTab={activeTab}
|
||||||
className="tw-flex-initial"
|
className="tw-flex-initial"
|
||||||
@ -838,7 +843,7 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</PageLayoutV1>
|
||||||
</PageContainerV1>
|
</PageContainerV1>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -224,14 +224,6 @@ jest.mock('../../utils/CommonUtils', () => ({
|
|||||||
getOwnerValue: jest.fn().mockReturnValue('Owner'),
|
getOwnerValue: jest.fn().mockReturnValue('Owner'),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const mockObserve = jest.fn();
|
|
||||||
const mockunObserve = jest.fn();
|
|
||||||
|
|
||||||
window.IntersectionObserver = jest.fn().mockImplementation(() => ({
|
|
||||||
observe: mockObserve,
|
|
||||||
unobserve: mockunObserve,
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('../PermissionProvider/PermissionProvider', () => ({
|
jest.mock('../PermissionProvider/PermissionProvider', () => ({
|
||||||
usePermissionProvider: jest.fn().mockImplementation(() => ({
|
usePermissionProvider: jest.fn().mockImplementation(() => ({
|
||||||
permissions: {},
|
permissions: {},
|
||||||
@ -285,6 +277,10 @@ jest.mock('../../utils/PermissionsUtils', () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('components/containers/PageLayoutV1', () => {
|
||||||
|
return jest.fn().mockImplementation(({ children }) => children);
|
||||||
|
});
|
||||||
|
|
||||||
describe('Test MyDataDetailsPage page', () => {
|
describe('Test MyDataDetailsPage page', () => {
|
||||||
it('Checks if the page has all the proper components rendered', async () => {
|
it('Checks if the page has all the proper components rendered', async () => {
|
||||||
const { container } = render(<DatasetDetails {...DatasetDetailsProps} />, {
|
const { container } = render(<DatasetDetails {...DatasetDetailsProps} />, {
|
||||||
@ -417,7 +413,5 @@ describe('Test MyDataDetailsPage page', () => {
|
|||||||
const obServerElement = await findByTestId(container, 'observer-element');
|
const obServerElement = await findByTestId(container, 'observer-element');
|
||||||
|
|
||||||
expect(obServerElement).toBeInTheDocument();
|
expect(obServerElement).toBeInTheDocument();
|
||||||
|
|
||||||
expect(mockObserve).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -390,10 +390,11 @@ const DatasetVersion: React.FC<DatasetVersionProp> = ({
|
|||||||
entityName={currentVersionData.name ?? ''}
|
entityName={currentVersionData.name ?? ''}
|
||||||
extraInfo={getExtraInfo()}
|
extraInfo={getExtraInfo()}
|
||||||
followersList={[]}
|
followersList={[]}
|
||||||
|
serviceType={currentVersionData.serviceType ?? ''}
|
||||||
tags={getTags()}
|
tags={getTags()}
|
||||||
tier={tier}
|
tier={tier}
|
||||||
titleLinks={slashedTableName}
|
titleLinks={slashedTableName}
|
||||||
version={version}
|
version={Number(version)}
|
||||||
versionHandler={backHandler}
|
versionHandler={backHandler}
|
||||||
/>
|
/>
|
||||||
<div className="tw-mt-1 tw-flex tw-flex-col tw-flex-grow ">
|
<div className="tw-mt-1 tw-flex tw-flex-col tw-flex-grow ">
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 Collate.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Col, Row } from 'antd';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component';
|
||||||
|
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
|
||||||
|
import { EntityType } from 'enums/entity.enum';
|
||||||
|
import React, { ReactNode } from 'react';
|
||||||
|
import { getEntityLinkFromType, getEntityName } from 'utils/EntityUtils';
|
||||||
|
import EntityHeaderTitle from '../EntityHeaderTitle/EntityHeaderTitle.component';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
extra?: ReactNode;
|
||||||
|
breadcrumb: TitleBreadcrumbProps['titleLinks'];
|
||||||
|
entityData: {
|
||||||
|
displayName?: string;
|
||||||
|
name: string;
|
||||||
|
fullyQualifiedName?: string;
|
||||||
|
deleted?: boolean;
|
||||||
|
};
|
||||||
|
entityType?: EntityType;
|
||||||
|
icon: ReactNode;
|
||||||
|
titleIsLink?: boolean;
|
||||||
|
openEntityInNewPage?: boolean;
|
||||||
|
gutter?: 'default' | 'large';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EntityHeader = ({
|
||||||
|
breadcrumb,
|
||||||
|
entityData,
|
||||||
|
extra,
|
||||||
|
icon,
|
||||||
|
titleIsLink = false,
|
||||||
|
entityType,
|
||||||
|
openEntityInNewPage,
|
||||||
|
gutter = 'default',
|
||||||
|
}: Props) => {
|
||||||
|
return (
|
||||||
|
<Row className="w-full" gutter={0} justify="space-between">
|
||||||
|
<Col>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'tw-text-link tw-text-base glossary-breadcrumb',
|
||||||
|
gutter === 'large' ? 'm-b-sm' : 'm-b-xss'
|
||||||
|
)}
|
||||||
|
data-testid="category-name">
|
||||||
|
<TitleBreadcrumb titleLinks={breadcrumb} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<EntityHeaderTitle
|
||||||
|
deleted={entityData.deleted}
|
||||||
|
displayName={getEntityName(entityData)}
|
||||||
|
icon={icon}
|
||||||
|
link={
|
||||||
|
titleIsLink && entityData.fullyQualifiedName && entityType
|
||||||
|
? getEntityLinkFromType(entityData.fullyQualifiedName, entityType)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
name={entityData.name}
|
||||||
|
openEntityInNewPage={openEntityInNewPage}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col>{extra}</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 Collate.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import { ExclamationCircleFilled } from '@ant-design/icons';
|
||||||
|
import { Col, Row, Typography } from 'antd';
|
||||||
|
import { ReactComponent as IconExternalLink } from 'assets/svg/external-link-grey.svg';
|
||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { EntityHeaderTitleProps } from './EntityHeaderTitle.interface';
|
||||||
|
|
||||||
|
const EntityHeaderTitle = ({
|
||||||
|
icon,
|
||||||
|
name,
|
||||||
|
displayName,
|
||||||
|
link,
|
||||||
|
openEntityInNewPage,
|
||||||
|
deleted = false,
|
||||||
|
}: EntityHeaderTitleProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row align="middle" gutter={8} wrap={false}>
|
||||||
|
<Col>{icon}</Col>
|
||||||
|
<Col>
|
||||||
|
<div>
|
||||||
|
<Typography.Text
|
||||||
|
className="m-b-0 d-block tw-text-xs tw-text-grey-muted"
|
||||||
|
data-testid="entity-header-name">
|
||||||
|
{name}
|
||||||
|
</Typography.Text>
|
||||||
|
{link ? (
|
||||||
|
<Link
|
||||||
|
className="m-b-0 d-block entity-header-display-name text-lg font-bold"
|
||||||
|
data-testid="entity-header-display-name"
|
||||||
|
target={openEntityInNewPage ? '_blank' : '_self'}
|
||||||
|
to={link}>
|
||||||
|
<Typography.Text ellipsis={{ tooltip: true }}>
|
||||||
|
{displayName ?? name}
|
||||||
|
{openEntityInNewPage && (
|
||||||
|
<IconExternalLink
|
||||||
|
className="anticon vertical-baseline m-l-xss"
|
||||||
|
height={14}
|
||||||
|
width={14}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Typography.Text>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<Typography.Text
|
||||||
|
className="m-b-0 d-block entity-header-display-name text-lg font-bold"
|
||||||
|
data-testid="entity-header-display-name"
|
||||||
|
ellipsis={{ tooltip: true }}>
|
||||||
|
{displayName ?? name}
|
||||||
|
</Typography.Text>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
{deleted && (
|
||||||
|
<Col className="self-end text-xs">
|
||||||
|
<div className="deleted-badge-button" data-testid="deleted-badge">
|
||||||
|
<ExclamationCircleFilled className="m-r-xss font-medium text-xs" />
|
||||||
|
{t('label.deleted')}
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EntityHeaderTitle;
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2022 Collate.
|
* Copyright 2023 Collate.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
@ -10,25 +10,11 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
@import url('../../../styles/variables.less');
|
export interface EntityHeaderTitleProps {
|
||||||
@link-btn-color: #37352f;
|
icon: React.ReactNode;
|
||||||
|
name: string;
|
||||||
.table-data-card-title-container {
|
displayName: string;
|
||||||
.ant-btn-link {
|
link?: string;
|
||||||
color: @link-btn-color;
|
openEntityInNewPage?: boolean;
|
||||||
font-weight: 600;
|
deleted?: boolean;
|
||||||
padding: 0px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
.ant-btn-link > span {
|
|
||||||
color: @link-btn-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-hover {
|
|
||||||
.ant-btn-link > span {
|
|
||||||
&:hover {
|
|
||||||
color: @primary-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 Collate.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
import EntityHeaderTitle from './EntityHeaderTitle.component';
|
||||||
|
|
||||||
|
describe('EntityHeaderTitle', () => {
|
||||||
|
it('should render icon', () => {
|
||||||
|
render(
|
||||||
|
<EntityHeaderTitle
|
||||||
|
displayName="Test DisplayName"
|
||||||
|
icon="test-icon"
|
||||||
|
name="test-name"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('test-icon')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render name', () => {
|
||||||
|
render(
|
||||||
|
<EntityHeaderTitle
|
||||||
|
displayName="Test DisplayName"
|
||||||
|
icon="test-icon"
|
||||||
|
name="test-name"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('test-name')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render displayName', () => {
|
||||||
|
render(
|
||||||
|
<EntityHeaderTitle
|
||||||
|
displayName="Test DisplayName"
|
||||||
|
icon="test-icon"
|
||||||
|
name="test-name"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('Test DisplayName')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render link if link is provided', () => {
|
||||||
|
render(
|
||||||
|
<EntityHeaderTitle
|
||||||
|
displayName="Test DisplayName"
|
||||||
|
icon="test-icon"
|
||||||
|
link="test-link"
|
||||||
|
name="test-name"
|
||||||
|
/>,
|
||||||
|
{ wrapper: MemoryRouter }
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByTestId('entity-header-display-name')).toHaveProperty(
|
||||||
|
'href',
|
||||||
|
'http://localhost/test-link'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2023 Collate.
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
import { Col, Row, Typography } from 'antd';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
interface props {
|
|
||||||
icon: React.ReactNode;
|
|
||||||
name: string;
|
|
||||||
displayName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const EntityHeaderTitle = ({ icon, name, displayName }: props) => {
|
|
||||||
return (
|
|
||||||
<Row align="middle" gutter={8} wrap={false}>
|
|
||||||
<Col>{icon}</Col>
|
|
||||||
<Col>
|
|
||||||
<div>
|
|
||||||
<Typography.Text
|
|
||||||
className="m-b-0 d-block tw-text-xs tw-text-grey-muted"
|
|
||||||
data-testid="entity-header-name">
|
|
||||||
{name}
|
|
||||||
</Typography.Text>
|
|
||||||
<Typography.Text
|
|
||||||
className="m-b-0 d-block entity-header-display-name text-lg font-bold"
|
|
||||||
data-testid="entity-header-display-name"
|
|
||||||
ellipsis={{ tooltip: true }}>
|
|
||||||
{displayName}
|
|
||||||
</Typography.Text>
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EntityHeaderTitle;
|
|
@ -12,19 +12,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
import { Col, Drawer, Row } from 'antd';
|
import { Drawer } from 'antd';
|
||||||
import TableDataCardTitle from 'components/common/table-data-card-v2/TableDataCardTitle.component';
|
import { EntityHeader } from 'components/Entity/EntityHeader/EntityHeader.component';
|
||||||
import { EntityType } from 'enums/entity.enum';
|
import { EntityType } from 'enums/entity.enum';
|
||||||
import { Tag } from 'generated/entity/classification/tag';
|
import { Tag } from 'generated/entity/classification/tag';
|
||||||
import { Container } from 'generated/entity/data/container';
|
import { Container } from 'generated/entity/data/container';
|
||||||
|
import { Dashboard } from 'generated/entity/data/dashboard';
|
||||||
import { GlossaryTerm } from 'generated/entity/data/glossaryTerm';
|
import { GlossaryTerm } from 'generated/entity/data/glossaryTerm';
|
||||||
|
import { Table } from 'generated/entity/data/table';
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { Dashboard } from '../../../generated/entity/data/dashboard';
|
import { getEntityBreadcrumbs } from 'utils/EntityUtils';
|
||||||
|
import { getServiceIcon } from 'utils/TableUtils';
|
||||||
import { Mlmodel } from '../../../generated/entity/data/mlmodel';
|
import { Mlmodel } from '../../../generated/entity/data/mlmodel';
|
||||||
import { Pipeline } from '../../../generated/entity/data/pipeline';
|
import { Pipeline } from '../../../generated/entity/data/pipeline';
|
||||||
import { Table } from '../../../generated/entity/data/table';
|
|
||||||
import { Topic } from '../../../generated/entity/data/topic';
|
import { Topic } from '../../../generated/entity/data/topic';
|
||||||
import ContainerSummary from './ContainerSummary/ContainerSummary.component';
|
import ContainerSummary from './ContainerSummary/ContainerSummary.component';
|
||||||
import DashboardSummary from './DashboardSummary/DashboardSummary.component';
|
import DashboardSummary from './DashboardSummary/DashboardSummary.component';
|
||||||
@ -42,70 +44,44 @@ export default function EntitySummaryPanel({
|
|||||||
handleClosePanel,
|
handleClosePanel,
|
||||||
}: EntitySummaryPanelProps) {
|
}: EntitySummaryPanelProps) {
|
||||||
const { tab } = useParams<{ tab: string }>();
|
const { tab } = useParams<{ tab: string }>();
|
||||||
const [currentSearchIndex, setCurrentSearchIndex] = useState<EntityType>();
|
|
||||||
|
|
||||||
const summaryComponent = useMemo(() => {
|
const summaryComponent = useMemo(() => {
|
||||||
const type = get(entityDetails, 'details.entityType') ?? EntityType.TABLE;
|
const type = get(entityDetails, 'details.entityType') ?? EntityType.TABLE;
|
||||||
|
const entity = entityDetails.details;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case EntityType.TABLE:
|
case EntityType.TABLE:
|
||||||
setCurrentSearchIndex(EntityType.TABLE);
|
return <TableSummary entityDetails={entity as Table} />;
|
||||||
|
|
||||||
return <TableSummary entityDetails={entityDetails.details as Table} />;
|
|
||||||
|
|
||||||
case EntityType.TOPIC:
|
case EntityType.TOPIC:
|
||||||
setCurrentSearchIndex(EntityType.TOPIC);
|
return <TopicSummary entityDetails={entity as Topic} />;
|
||||||
|
|
||||||
return <TopicSummary entityDetails={entityDetails.details as Topic} />;
|
|
||||||
|
|
||||||
case EntityType.DASHBOARD:
|
case EntityType.DASHBOARD:
|
||||||
setCurrentSearchIndex(EntityType.DASHBOARD);
|
return <DashboardSummary entityDetails={entity as Dashboard} />;
|
||||||
|
|
||||||
return (
|
|
||||||
<DashboardSummary
|
|
||||||
entityDetails={entityDetails.details as Dashboard}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
case EntityType.PIPELINE:
|
case EntityType.PIPELINE:
|
||||||
setCurrentSearchIndex(EntityType.PIPELINE);
|
return <PipelineSummary entityDetails={entity as Pipeline} />;
|
||||||
|
|
||||||
return (
|
|
||||||
<PipelineSummary entityDetails={entityDetails.details as Pipeline} />
|
|
||||||
);
|
|
||||||
|
|
||||||
case EntityType.MLMODEL:
|
case EntityType.MLMODEL:
|
||||||
setCurrentSearchIndex(EntityType.MLMODEL);
|
return <MlModelSummary entityDetails={entity as Mlmodel} />;
|
||||||
|
|
||||||
return (
|
|
||||||
<MlModelSummary entityDetails={entityDetails.details as Mlmodel} />
|
|
||||||
);
|
|
||||||
|
|
||||||
case EntityType.CONTAINER:
|
case EntityType.CONTAINER:
|
||||||
setCurrentSearchIndex(EntityType.CONTAINER);
|
return <ContainerSummary entityDetails={entity as Container} />;
|
||||||
|
|
||||||
return (
|
|
||||||
<ContainerSummary
|
|
||||||
entityDetails={entityDetails.details as Container}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case EntityType.GLOSSARY_TERM:
|
case EntityType.GLOSSARY_TERM:
|
||||||
setCurrentSearchIndex(EntityType.GLOSSARY);
|
return <GlossaryTermSummary entityDetails={entity as GlossaryTerm} />;
|
||||||
|
|
||||||
return (
|
|
||||||
<GlossaryTermSummary
|
|
||||||
entityDetails={entityDetails.details as GlossaryTerm}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case EntityType.TAG:
|
case EntityType.TAG:
|
||||||
setCurrentSearchIndex(EntityType.TAG);
|
return <TagsSummary entityDetails={entity as Tag} />;
|
||||||
|
|
||||||
return <TagsSummary entityDetails={entityDetails.details as Tag} />;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}, [tab, entityDetails]);
|
}, [tab, entityDetails]);
|
||||||
|
|
||||||
|
const icon = useMemo(() => {
|
||||||
|
return getServiceIcon(entityDetails.details);
|
||||||
|
}, [entityDetails]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
@ -122,16 +98,16 @@ export default function EntitySummaryPanel({
|
|||||||
headerStyle={{ padding: 16 }}
|
headerStyle={{ padding: 16 }}
|
||||||
mask={false}
|
mask={false}
|
||||||
title={
|
title={
|
||||||
<Row gutter={[0, 6]}>
|
<EntityHeader
|
||||||
<Col span={24}>
|
titleIsLink
|
||||||
<TableDataCardTitle
|
breadcrumb={getEntityBreadcrumbs(
|
||||||
isPanel
|
entityDetails.details,
|
||||||
dataTestId="summary-panel-title"
|
entityDetails.details.entityType as EntityType
|
||||||
searchIndex={currentSearchIndex as EntityType}
|
)}
|
||||||
source={entityDetails.details}
|
entityData={entityDetails.details}
|
||||||
/>
|
entityType={entityDetails.details.entityType as EntityType}
|
||||||
</Col>
|
icon={icon}
|
||||||
</Row>
|
/>
|
||||||
}
|
}
|
||||||
width="100%">
|
width="100%">
|
||||||
{summaryComponent}
|
{summaryComponent}
|
||||||
|
@ -64,36 +64,30 @@ jest.mock('./MlModelSummary/MlModelSummary.component', () =>
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
jest.mock(
|
|
||||||
'components/common/table-data-card-v2/TableDataCardTitle.component',
|
|
||||||
() =>
|
|
||||||
jest
|
|
||||||
.fn()
|
|
||||||
.mockImplementation(() => (
|
|
||||||
<div data-testid="table-data-card-title">TableDataCardTitle</div>
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
jest.mock('react-router-dom', () => ({
|
jest.mock('react-router-dom', () => ({
|
||||||
useParams: jest.fn().mockImplementation(() => ({ tab: 'table' })),
|
useParams: jest.fn().mockImplementation(() => ({ tab: 'table' })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('components/Entity/EntityHeader/EntityHeader.component', () => ({
|
||||||
|
EntityHeader: jest.fn().mockImplementation(() => <p>EntityHeader</p>),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('EntitySummaryPanel component tests', () => {
|
describe('EntitySummaryPanel component tests', () => {
|
||||||
it('TableSummary should render for table data', async () => {
|
it('TableSummary should render for table data', async () => {
|
||||||
render(
|
render(
|
||||||
<EntitySummaryPanel
|
<EntitySummaryPanel
|
||||||
entityDetails={{
|
entityDetails={{
|
||||||
details: mockTableEntityDetails,
|
details: { ...mockTableEntityDetails, entityType: EntityType.TABLE },
|
||||||
}}
|
}}
|
||||||
handleClosePanel={mockHandleClosePanel}
|
handleClosePanel={mockHandleClosePanel}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const tableDataCardTitle = screen.getByText('TableDataCardTitle');
|
const entityHeader = screen.getByText('EntityHeader');
|
||||||
const tableSummary = screen.getByTestId('TableSummary');
|
const tableSummary = screen.getByTestId('TableSummary');
|
||||||
const closeIcon = screen.getByTestId('summary-panel-close-icon');
|
const closeIcon = screen.getByTestId('summary-panel-close-icon');
|
||||||
|
|
||||||
expect(tableDataCardTitle).toBeInTheDocument();
|
expect(entityHeader).toBeInTheDocument();
|
||||||
expect(tableSummary).toBeInTheDocument();
|
expect(tableSummary).toBeInTheDocument();
|
||||||
expect(closeIcon).toBeInTheDocument();
|
expect(closeIcon).toBeInTheDocument();
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ function SummaryListItem({
|
|||||||
{entityDetails.title}
|
{entityDetails.title}
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Row className="text-xs font-300" gutter={[4, 4]}>
|
<Row className="text-xs font-thin" gutter={[4, 4]}>
|
||||||
<Col>
|
<Col>
|
||||||
{entityDetails.type && (
|
{entityDetails.type && (
|
||||||
<Space size={4}>
|
<Space size={4}>
|
||||||
|
@ -18,7 +18,6 @@ import { EntityUnion } from 'components/Explore/explore.interface';
|
|||||||
import { SourceType } from 'components/searched-data/SearchedData.interface';
|
import { SourceType } from 'components/searched-data/SearchedData.interface';
|
||||||
import SummaryPanelSkeleton from 'components/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component';
|
import SummaryPanelSkeleton from 'components/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component';
|
||||||
import { SearchIndex } from 'enums/search.enum';
|
import { SearchIndex } from 'enums/search.enum';
|
||||||
import { get } from 'lodash';
|
|
||||||
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 { searchData } from 'rest/miscAPI';
|
import { searchData } from 'rest/miscAPI';
|
||||||
@ -50,14 +49,11 @@ function TagsSummary({ entityDetails, isLoading }: TagsSummaryProps) {
|
|||||||
|
|
||||||
const usageItems = useMemo(() => {
|
const usageItems = useMemo(() => {
|
||||||
return selectedData.map((entity, index) => {
|
return selectedData.map((entity, index) => {
|
||||||
const searchIndex = get(entity, 'entityType');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<TableDataCardV2
|
<TableDataCardV2
|
||||||
id={`tabledatacardtest${index}`}
|
id={`tabledatacardtest${index}`}
|
||||||
searchIndex={searchIndex}
|
|
||||||
source={entity as SourceType}
|
source={entity as SourceType}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -127,6 +127,13 @@ export type EntityUnion =
|
|||||||
| Tag
|
| Tag
|
||||||
| DashboardDataModel;
|
| DashboardDataModel;
|
||||||
|
|
||||||
|
export type EntityWithServices =
|
||||||
|
| Topic
|
||||||
|
| Dashboard
|
||||||
|
| Pipeline
|
||||||
|
| Mlmodel
|
||||||
|
| Container;
|
||||||
|
|
||||||
export interface EntityDetailsObjectInterface {
|
export interface EntityDetailsObjectInterface {
|
||||||
details: SearchedDataProps['data'][number]['_source'];
|
details: SearchedDataProps['data'][number]['_source'];
|
||||||
}
|
}
|
||||||
|
@ -13,16 +13,15 @@
|
|||||||
import { Col, Row } from 'antd';
|
import { Col, Row } from 'antd';
|
||||||
import { ReactComponent as IconFolder } from 'assets/svg/folder.svg';
|
import { ReactComponent as IconFolder } from 'assets/svg/folder.svg';
|
||||||
import { ReactComponent as IconFlatDoc } from 'assets/svg/ic-flat-doc.svg';
|
import { ReactComponent as IconFlatDoc } from 'assets/svg/ic-flat-doc.svg';
|
||||||
import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component';
|
|
||||||
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
|
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
|
||||||
import EntityHeaderTitle from 'components/EntityHeaderTitle/EntityHeaderTitle.component';
|
import { EntityHeader } from 'components/Entity/EntityHeader/EntityHeader.component';
|
||||||
import { OperationPermission } from 'components/PermissionProvider/PermissionProvider.interface';
|
import { OperationPermission } from 'components/PermissionProvider/PermissionProvider.interface';
|
||||||
import { FQN_SEPARATOR_CHAR } from 'constants/char.constants';
|
import { FQN_SEPARATOR_CHAR } from 'constants/char.constants';
|
||||||
import { DE_ACTIVE_COLOR } from 'constants/constants';
|
import { DE_ACTIVE_COLOR } from 'constants/constants';
|
||||||
|
import { EntityType } from 'enums/entity.enum';
|
||||||
import { Glossary } from 'generated/entity/data/glossary';
|
import { Glossary } from 'generated/entity/data/glossary';
|
||||||
import { GlossaryTerm } from 'generated/entity/data/glossaryTerm';
|
import { GlossaryTerm } from 'generated/entity/data/glossaryTerm';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { getEntityName } from 'utils/EntityUtils';
|
|
||||||
import { getGlossaryPath } from 'utils/RouterUtils';
|
import { getGlossaryPath } from 'utils/RouterUtils';
|
||||||
import GlossaryHeaderButtons from '../GlossaryHeaderButtons/GlossaryHeaderButtons.component';
|
import GlossaryHeaderButtons from '../GlossaryHeaderButtons/GlossaryHeaderButtons.component';
|
||||||
|
|
||||||
@ -33,7 +32,7 @@ export interface GlossaryHeaderProps {
|
|||||||
isGlossary: boolean;
|
isGlossary: boolean;
|
||||||
onUpdate: (data: GlossaryTerm | Glossary) => void;
|
onUpdate: (data: GlossaryTerm | Glossary) => void;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
onAssetsUpdate?: () => void;
|
onAssetAdd?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GlossaryHeader = ({
|
const GlossaryHeader = ({
|
||||||
@ -42,7 +41,7 @@ const GlossaryHeader = ({
|
|||||||
onUpdate,
|
onUpdate,
|
||||||
onDelete,
|
onDelete,
|
||||||
isGlossary,
|
isGlossary,
|
||||||
onAssetsUpdate,
|
onAssetAdd,
|
||||||
}: GlossaryHeaderProps) => {
|
}: GlossaryHeaderProps) => {
|
||||||
const [breadcrumb, setBreadcrumb] = useState<
|
const [breadcrumb, setBreadcrumb] = useState<
|
||||||
TitleBreadcrumbProps['titleLinks']
|
TitleBreadcrumbProps['titleLinks']
|
||||||
@ -88,50 +87,42 @@ const GlossaryHeader = ({
|
|||||||
<>
|
<>
|
||||||
<Row gutter={[0, 16]}>
|
<Row gutter={[0, 16]}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Row justify="space-between">
|
<EntityHeader
|
||||||
<Col span={12}>
|
breadcrumb={breadcrumb}
|
||||||
<div
|
entityData={selectedData}
|
||||||
className="tw-text-link tw-text-base glossary-breadcrumb m-b-sm"
|
entityType={EntityType.GLOSSARY_TERM}
|
||||||
data-testid="category-name">
|
extra={
|
||||||
<TitleBreadcrumb titleLinks={breadcrumb} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<EntityHeaderTitle
|
|
||||||
displayName={getEntityName(selectedData)}
|
|
||||||
icon={
|
|
||||||
isGlossary ? (
|
|
||||||
<IconFolder
|
|
||||||
color={DE_ACTIVE_COLOR}
|
|
||||||
height={36}
|
|
||||||
name="folder"
|
|
||||||
width={32}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<IconFlatDoc
|
|
||||||
color={DE_ACTIVE_COLOR}
|
|
||||||
height={36}
|
|
||||||
name="doc"
|
|
||||||
width={32}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
name={selectedData.name}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col span={12}>
|
|
||||||
<div style={{ textAlign: 'right' }}>
|
<div style={{ textAlign: 'right' }}>
|
||||||
<GlossaryHeaderButtons
|
<GlossaryHeaderButtons
|
||||||
deleteStatus="success"
|
deleteStatus="success"
|
||||||
isGlossary={isGlossary}
|
isGlossary={isGlossary}
|
||||||
permission={permissions}
|
permission={permissions}
|
||||||
selectedData={selectedData}
|
selectedData={selectedData}
|
||||||
onAssetsUpdate={onAssetsUpdate}
|
onAssetAdd={onAssetAdd}
|
||||||
onEntityDelete={onDelete}
|
onEntityDelete={onDelete}
|
||||||
onUpdate={onUpdate}
|
onUpdate={onUpdate}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
}
|
||||||
</Row>
|
gutter="large"
|
||||||
|
icon={
|
||||||
|
isGlossary ? (
|
||||||
|
<IconFolder
|
||||||
|
color={DE_ACTIVE_COLOR}
|
||||||
|
height={36}
|
||||||
|
name="folder"
|
||||||
|
width={32}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<IconFlatDoc
|
||||||
|
color={DE_ACTIVE_COLOR}
|
||||||
|
height={36}
|
||||||
|
name="doc"
|
||||||
|
width={32}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</>
|
</>
|
||||||
|
@ -16,7 +16,6 @@ import { ReactComponent as EditIcon } from 'assets/svg/edit-new.svg';
|
|||||||
import { ReactComponent as ExportIcon } from 'assets/svg/ic-export.svg';
|
import { ReactComponent as ExportIcon } from 'assets/svg/ic-export.svg';
|
||||||
import { ReactComponent as ImportIcon } from 'assets/svg/ic-import.svg';
|
import { ReactComponent as ImportIcon } from 'assets/svg/ic-import.svg';
|
||||||
import { ReactComponent as IconDropdown } from 'assets/svg/menu.svg';
|
import { ReactComponent as IconDropdown } from 'assets/svg/menu.svg';
|
||||||
import { AssetSelectionModal } from 'components/Assets/AssetsSelectionModal/AssetSelectionModal';
|
|
||||||
import EntityDeleteModal from 'components/Modals/EntityDeleteModal/EntityDeleteModal';
|
import EntityDeleteModal from 'components/Modals/EntityDeleteModal/EntityDeleteModal';
|
||||||
import EntityNameModal from 'components/Modals/EntityNameModal/EntityNameModal.component';
|
import EntityNameModal from 'components/Modals/EntityNameModal/EntityNameModal.component';
|
||||||
import { OperationPermission } from 'components/PermissionProvider/PermissionProvider.interface';
|
import { OperationPermission } from 'components/PermissionProvider/PermissionProvider.interface';
|
||||||
@ -48,7 +47,7 @@ interface GlossaryHeaderButtonsProps {
|
|||||||
selectedData: Glossary | GlossaryTerm;
|
selectedData: Glossary | GlossaryTerm;
|
||||||
permission: OperationPermission;
|
permission: OperationPermission;
|
||||||
onEntityDelete: (id: string) => void;
|
onEntityDelete: (id: string) => void;
|
||||||
onAssetsUpdate?: () => void;
|
onAssetAdd?: () => void;
|
||||||
onUpdate: (data: GlossaryTerm | Glossary) => void;
|
onUpdate: (data: GlossaryTerm | Glossary) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +57,7 @@ const GlossaryHeaderButtons = ({
|
|||||||
selectedData,
|
selectedData,
|
||||||
permission,
|
permission,
|
||||||
onEntityDelete,
|
onEntityDelete,
|
||||||
onAssetsUpdate,
|
onAssetAdd,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
}: GlossaryHeaderButtonsProps) => {
|
}: GlossaryHeaderButtonsProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -75,7 +74,7 @@ const GlossaryHeaderButtons = ({
|
|||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [showActions, setShowActions] = useState(false);
|
const [showActions, setShowActions] = useState(false);
|
||||||
const [isDelete, setIsDelete] = useState<boolean>(false);
|
const [isDelete, setIsDelete] = useState<boolean>(false);
|
||||||
const [showAddAssets, setShowAddAssets] = useState(false);
|
|
||||||
const [isNameEditing, setIsNameEditing] = useState<boolean>(false);
|
const [isNameEditing, setIsNameEditing] = useState<boolean>(false);
|
||||||
|
|
||||||
const editDisplayNamePermission = useMemo(() => {
|
const editDisplayNamePermission = useMemo(() => {
|
||||||
@ -133,10 +132,6 @@ const GlossaryHeaderButtons = ({
|
|||||||
setIsDelete(false);
|
setIsDelete(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddAssetsClick = () => {
|
|
||||||
setShowAddAssets(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onNameSave = (obj: { name: string; displayName: string }) => {
|
const onNameSave = (obj: { name: string; displayName: string }) => {
|
||||||
const { name, displayName } = obj;
|
const { name, displayName } = obj;
|
||||||
let updatedDetails = cloneDeep(selectedData);
|
let updatedDetails = cloneDeep(selectedData);
|
||||||
@ -161,7 +156,7 @@ const GlossaryHeaderButtons = ({
|
|||||||
{
|
{
|
||||||
label: t('label.asset-plural'),
|
label: t('label.asset-plural'),
|
||||||
key: '2',
|
key: '2',
|
||||||
onClick: () => handleAddAssetsClick(),
|
onClick: onAssetAdd,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -388,14 +383,7 @@ const GlossaryHeaderButtons = ({
|
|||||||
onOk={handleCancelGlossaryExport}
|
onOk={handleCancelGlossaryExport}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{selectedData.fullyQualifiedName && !isGlossary && (
|
|
||||||
<AssetSelectionModal
|
|
||||||
glossaryFQN={selectedData.fullyQualifiedName}
|
|
||||||
open={showAddAssets}
|
|
||||||
onCancel={() => setShowAddAssets(false)}
|
|
||||||
onSave={onAssetsUpdate}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<EntityNameModal
|
<EntityNameModal
|
||||||
entity={selectedData as EntityReference}
|
entity={selectedData as EntityReference}
|
||||||
visible={isNameEditing}
|
visible={isNameEditing}
|
||||||
|
@ -19,12 +19,10 @@ import AssetsTabs, {
|
|||||||
import GlossaryOverviewTab from 'components/GlossaryTerms/tabs/GlossaryOverviewTab.component';
|
import GlossaryOverviewTab from 'components/GlossaryTerms/tabs/GlossaryOverviewTab.component';
|
||||||
import { OperationPermission } from 'components/PermissionProvider/PermissionProvider.interface';
|
import { OperationPermission } from 'components/PermissionProvider/PermissionProvider.interface';
|
||||||
import { getGlossaryTermDetailsPath } from 'constants/constants';
|
import { getGlossaryTermDetailsPath } from 'constants/constants';
|
||||||
import { myDataSearchIndex } from 'constants/Mydata.constants';
|
|
||||||
import { Glossary } from 'generated/entity/data/glossary';
|
import { Glossary } from 'generated/entity/data/glossary';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import React, { RefObject, useEffect, useMemo, useState } from 'react';
|
import React, { RefObject, useMemo } from 'react';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { searchData } from 'rest/miscAPI';
|
|
||||||
import { getGlossaryTermsVersionsPath } from 'utils/RouterUtils';
|
import { getGlossaryTermsVersionsPath } from 'utils/RouterUtils';
|
||||||
import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm';
|
import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm';
|
||||||
import { getCountBadge } from '../../utils/CommonUtils';
|
import { getCountBadge } from '../../utils/CommonUtils';
|
||||||
@ -34,12 +32,13 @@ type Props = {
|
|||||||
childGlossaryTerms: GlossaryTerm[];
|
childGlossaryTerms: GlossaryTerm[];
|
||||||
permissions: OperationPermission;
|
permissions: OperationPermission;
|
||||||
isGlossary: boolean;
|
isGlossary: boolean;
|
||||||
|
onAddAsset: () => void;
|
||||||
onUpdate: (data: GlossaryTerm | Glossary) => void;
|
onUpdate: (data: GlossaryTerm | Glossary) => void;
|
||||||
refreshGlossaryTerms: () => void;
|
refreshGlossaryTerms: () => void;
|
||||||
onAssetClick?: (asset?: EntityDetailsObjectInterface) => void;
|
onAssetClick?: (asset?: EntityDetailsObjectInterface) => void;
|
||||||
assetsRef: RefObject<AssetsTabRef>;
|
assetsRef: RefObject<AssetsTabRef>;
|
||||||
isSummaryPanelOpen: boolean;
|
isSummaryPanelOpen: boolean;
|
||||||
|
assetCount?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const GlossaryTabs = ({
|
const GlossaryTabs = ({
|
||||||
@ -47,10 +46,12 @@ const GlossaryTabs = ({
|
|||||||
childGlossaryTerms,
|
childGlossaryTerms,
|
||||||
isGlossary,
|
isGlossary,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
|
onAddAsset,
|
||||||
permissions,
|
permissions,
|
||||||
refreshGlossaryTerms,
|
refreshGlossaryTerms,
|
||||||
onAssetClick,
|
onAssetClick,
|
||||||
assetsRef,
|
assetsRef,
|
||||||
|
assetCount,
|
||||||
isSummaryPanelOpen,
|
isSummaryPanelOpen,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
@ -59,7 +60,6 @@ const GlossaryTabs = ({
|
|||||||
version,
|
version,
|
||||||
} = useParams<{ glossaryName: string; tab: string; version: string }>();
|
} = useParams<{ glossaryName: string; tab: string; version: string }>();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [assetCount, setAssetCount] = useState<number>(0);
|
|
||||||
|
|
||||||
const activeTabHandler = (tab: string) => {
|
const activeTabHandler = (tab: string) => {
|
||||||
history.push({
|
history.push({
|
||||||
@ -69,30 +69,6 @@ const GlossaryTabs = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchGlossaryTermAssets = async () => {
|
|
||||||
if (glossaryFqn) {
|
|
||||||
try {
|
|
||||||
const res = await searchData(
|
|
||||||
'',
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
`(tags.tagFQN:"${glossaryFqn}")`,
|
|
||||||
'',
|
|
||||||
'',
|
|
||||||
myDataSearchIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
setAssetCount(res.data.hits.total.value ?? 0);
|
|
||||||
} catch (error) {
|
|
||||||
setAssetCount(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchGlossaryTermAssets();
|
|
||||||
}, [glossaryFqn]);
|
|
||||||
|
|
||||||
const activeTab = useMemo(() => {
|
const activeTab = useMemo(() => {
|
||||||
return tab ?? 'overview';
|
return tab ?? 'overview';
|
||||||
}, [tab]);
|
}, [tab]);
|
||||||
@ -144,7 +120,7 @@ const GlossaryTabs = ({
|
|||||||
<div data-testid="assets">
|
<div data-testid="assets">
|
||||||
{t('label.asset-plural')}
|
{t('label.asset-plural')}
|
||||||
<span className="p-l-xs ">
|
<span className="p-l-xs ">
|
||||||
{getCountBadge(assetCount, '', activeTab === 'assets')}
|
{getCountBadge(assetCount ?? 0, '', activeTab === 'assets')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
@ -154,6 +130,7 @@ const GlossaryTabs = ({
|
|||||||
isSummaryPanelOpen={isSummaryPanelOpen}
|
isSummaryPanelOpen={isSummaryPanelOpen}
|
||||||
permissions={permissions}
|
permissions={permissions}
|
||||||
ref={assetsRef}
|
ref={assetsRef}
|
||||||
|
onAddAsset={onAddAsset}
|
||||||
onAssetClick={onAssetClick}
|
onAssetClick={onAssetClick}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
@ -12,10 +12,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Col, Row } from 'antd';
|
import { Col, Row } from 'antd';
|
||||||
|
import { AssetSelectionModal } from 'components/Assets/AssetsSelectionModal/AssetSelectionModal';
|
||||||
import { EntityDetailsObjectInterface } from 'components/Explore/explore.interface';
|
import { EntityDetailsObjectInterface } from 'components/Explore/explore.interface';
|
||||||
import GlossaryHeader from 'components/Glossary/GlossaryHeader/GlossaryHeader.component';
|
import GlossaryHeader from 'components/Glossary/GlossaryHeader/GlossaryHeader.component';
|
||||||
import GlossaryTabs from 'components/GlossaryTabs/GlossaryTabs.component';
|
import GlossaryTabs from 'components/GlossaryTabs/GlossaryTabs.component';
|
||||||
import React, { useRef } from 'react';
|
import { myDataSearchIndex } from 'constants/Mydata.constants';
|
||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { searchData } from 'rest/miscAPI';
|
||||||
import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm';
|
import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm';
|
||||||
import { OperationPermission } from '../PermissionProvider/PermissionProvider.interface';
|
import { OperationPermission } from '../PermissionProvider/PermissionProvider.interface';
|
||||||
import { AssetsTabRef } from './tabs/AssetsTabs.component';
|
import { AssetsTabRef } from './tabs/AssetsTabs.component';
|
||||||
@ -41,39 +45,80 @@ const GlossaryTermsV1 = ({
|
|||||||
onAssetClick,
|
onAssetClick,
|
||||||
isSummaryPanelOpen,
|
isSummaryPanelOpen,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
|
const { glossaryName: glossaryFqn } = useParams<{ glossaryName: string }>();
|
||||||
const assetTabRef = useRef<AssetsTabRef>(null);
|
const assetTabRef = useRef<AssetsTabRef>(null);
|
||||||
|
const [assetModalVisible, setAssetModelVisible] = useState(false);
|
||||||
|
|
||||||
|
const [assetCount, setAssetCount] = useState<number>(0);
|
||||||
|
|
||||||
|
const fetchGlossaryTermAssets = async () => {
|
||||||
|
if (glossaryFqn) {
|
||||||
|
try {
|
||||||
|
const res = await searchData(
|
||||||
|
'',
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
`(tags.tagFQN:"${glossaryFqn}")`,
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
myDataSearchIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
setAssetCount(res.data.hits.total.value ?? 0);
|
||||||
|
} catch (error) {
|
||||||
|
setAssetCount(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchGlossaryTermAssets();
|
||||||
|
}, [glossaryFqn]);
|
||||||
|
|
||||||
|
const handleAssetSave = () => {
|
||||||
|
fetchGlossaryTermAssets();
|
||||||
|
assetTabRef.current?.refreshAssets();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row data-testid="glossary-term" gutter={[0, 8]}>
|
<>
|
||||||
<Col span={24}>
|
<Row data-testid="glossary-term" gutter={[0, 8]}>
|
||||||
<GlossaryHeader
|
<Col span={24}>
|
||||||
isGlossary={false}
|
<GlossaryHeader
|
||||||
permissions={permissions}
|
isGlossary={false}
|
||||||
selectedData={glossaryTerm}
|
permissions={permissions}
|
||||||
onAssetsUpdate={() => {
|
selectedData={glossaryTerm}
|
||||||
if (glossaryTerm.fullyQualifiedName) {
|
onAssetAdd={() => setAssetModelVisible(true)}
|
||||||
assetTabRef.current?.refreshAssets();
|
onDelete={handleGlossaryTermDelete}
|
||||||
}
|
onUpdate={(data) => handleGlossaryTermUpdate(data as GlossaryTerm)}
|
||||||
}}
|
/>
|
||||||
onDelete={handleGlossaryTermDelete}
|
</Col>
|
||||||
onUpdate={(data) => handleGlossaryTermUpdate(data as GlossaryTerm)}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
|
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<GlossaryTabs
|
<GlossaryTabs
|
||||||
assetsRef={assetTabRef}
|
assetCount={assetCount}
|
||||||
childGlossaryTerms={childGlossaryTerms}
|
assetsRef={assetTabRef}
|
||||||
isGlossary={false}
|
childGlossaryTerms={childGlossaryTerms}
|
||||||
isSummaryPanelOpen={isSummaryPanelOpen}
|
isGlossary={false}
|
||||||
permissions={permissions}
|
isSummaryPanelOpen={isSummaryPanelOpen}
|
||||||
refreshGlossaryTerms={refreshGlossaryTerms}
|
permissions={permissions}
|
||||||
selectedData={glossaryTerm}
|
refreshGlossaryTerms={refreshGlossaryTerms}
|
||||||
onAssetClick={onAssetClick}
|
selectedData={glossaryTerm}
|
||||||
onUpdate={(data) => handleGlossaryTermUpdate(data as GlossaryTerm)}
|
onAddAsset={() => setAssetModelVisible(true)}
|
||||||
|
onAssetClick={onAssetClick}
|
||||||
|
onUpdate={(data) => handleGlossaryTermUpdate(data as GlossaryTerm)}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
{glossaryTerm.fullyQualifiedName && (
|
||||||
|
<AssetSelectionModal
|
||||||
|
glossaryFQN={glossaryTerm.fullyQualifiedName}
|
||||||
|
open={assetModalVisible}
|
||||||
|
onCancel={() => setAssetModelVisible(false)}
|
||||||
|
onSave={handleAssetSave}
|
||||||
/>
|
/>
|
||||||
</Col>
|
)}
|
||||||
</Row>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum';
|
|||||||
import { EntityType } from 'enums/entity.enum';
|
import { EntityType } from 'enums/entity.enum';
|
||||||
import { SearchIndex } from 'enums/search.enum';
|
import { SearchIndex } from 'enums/search.enum';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { startCase } from 'lodash';
|
import { find, startCase } from 'lodash';
|
||||||
import React, {
|
import React, {
|
||||||
forwardRef,
|
forwardRef,
|
||||||
useCallback,
|
useCallback,
|
||||||
@ -46,6 +46,7 @@ import { getCountBadge } from '../../../utils/CommonUtils';
|
|||||||
import ErrorPlaceHolder from '../../common/error-with-placeholder/ErrorPlaceHolder';
|
import ErrorPlaceHolder from '../../common/error-with-placeholder/ErrorPlaceHolder';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
onAddAsset: () => void;
|
||||||
permissions: OperationPermission;
|
permissions: OperationPermission;
|
||||||
onAssetClick?: (asset?: EntityDetailsObjectInterface) => void;
|
onAssetClick?: (asset?: EntityDetailsObjectInterface) => void;
|
||||||
isSummaryPanelOpen: boolean;
|
isSummaryPanelOpen: boolean;
|
||||||
@ -57,7 +58,10 @@ export interface AssetsTabRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AssetsTabs = forwardRef(
|
const AssetsTabs = forwardRef(
|
||||||
({ permissions, onAssetClick, isSummaryPanelOpen }: Props, ref) => {
|
(
|
||||||
|
{ permissions, onAssetClick, isSummaryPanelOpen, onAddAsset }: Props,
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
const [itemCount, setItemCount] = useState<Record<AssetsUnion, number>>({
|
const [itemCount, setItemCount] = useState<Record<AssetsUnion, number>>({
|
||||||
table: 0,
|
table: 0,
|
||||||
pipeline: 0,
|
pipeline: 0,
|
||||||
@ -98,22 +102,32 @@ const AssetsTabs = forwardRef(
|
|||||||
mlmodelResponse,
|
mlmodelResponse,
|
||||||
containerResponse,
|
containerResponse,
|
||||||
]) => {
|
]) => {
|
||||||
setItemCount({
|
const counts = {
|
||||||
[EntityType.TOPIC]: topicResponse.data.hits.total.value,
|
[EntityType.TOPIC]: topicResponse.data.hits.total.value,
|
||||||
[EntityType.TABLE]: tableResponse.data.hits.total.value,
|
[EntityType.TABLE]: tableResponse.data.hits.total.value,
|
||||||
[EntityType.DASHBOARD]: dashboardResponse.data.hits.total.value,
|
[EntityType.DASHBOARD]: dashboardResponse.data.hits.total.value,
|
||||||
[EntityType.PIPELINE]: pipelineResponse.data.hits.total.value,
|
[EntityType.PIPELINE]: pipelineResponse.data.hits.total.value,
|
||||||
[EntityType.MLMODEL]: mlmodelResponse.data.hits.total.value,
|
[EntityType.MLMODEL]: mlmodelResponse.data.hits.total.value,
|
||||||
[EntityType.CONTAINER]: containerResponse.data.hits.total.value,
|
[EntityType.CONTAINER]: containerResponse.data.hits.total.value,
|
||||||
});
|
};
|
||||||
|
setItemCount(counts);
|
||||||
|
|
||||||
setActiveFilter(
|
find(counts, (count, key) => {
|
||||||
tableResponse.data.hits.total.value
|
if (count > 0) {
|
||||||
? SearchIndex.TABLE
|
key;
|
||||||
: topicResponse.data.hits.total.value
|
|
||||||
? SearchIndex.TOPIC
|
const option = AssetsFilterOptions.find(
|
||||||
: SearchIndex.DASHBOARD
|
(el) => el.label === key
|
||||||
);
|
);
|
||||||
|
if (option) {
|
||||||
|
setActiveFilter(option.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@ -236,7 +250,7 @@ const AssetsTabs = forwardRef(
|
|||||||
})}
|
})}
|
||||||
{data.length ? (
|
{data.length ? (
|
||||||
<>
|
<>
|
||||||
{data.map(({ _source, _index, _id = '' }, index) => (
|
{data.map(({ _source, _id = '' }, index) => (
|
||||||
<TableDataCardV2
|
<TableDataCardV2
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'm-b-sm cursor-pointer',
|
'm-b-sm cursor-pointer',
|
||||||
@ -245,7 +259,6 @@ const AssetsTabs = forwardRef(
|
|||||||
handleSummaryPanelDisplay={setSelectedCard}
|
handleSummaryPanelDisplay={setSelectedCard}
|
||||||
id={_id}
|
id={_id}
|
||||||
key={index}
|
key={index}
|
||||||
searchIndex={_index as SearchIndex}
|
|
||||||
source={_source}
|
source={_source}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@ -271,7 +284,8 @@ const AssetsTabs = forwardRef(
|
|||||||
<Button
|
<Button
|
||||||
ghost
|
ghost
|
||||||
data-testid="add-new-asset-button"
|
data-testid="add-new-asset-button"
|
||||||
type="primary">
|
type="primary"
|
||||||
|
onClick={onAddAsset}>
|
||||||
{t('label.add-entity', {
|
{t('label.add-entity', {
|
||||||
entity: t('label.asset'),
|
entity: t('label.asset'),
|
||||||
})}
|
})}
|
||||||
|
@ -56,7 +56,6 @@ import {
|
|||||||
} from '../../utils/CommonUtils';
|
} from '../../utils/CommonUtils';
|
||||||
import { getEntityFieldThreadCounts } from '../../utils/FeedUtils';
|
import { getEntityFieldThreadCounts } from '../../utils/FeedUtils';
|
||||||
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
||||||
import { serviceTypeLogo } from '../../utils/ServiceUtils';
|
|
||||||
import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils';
|
import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils';
|
||||||
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
|
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
|
||||||
import ActivityFeedList from '../ActivityFeed/ActivityFeedList/ActivityFeedList';
|
import ActivityFeedList from '../ActivityFeed/ActivityFeedList/ActivityFeedList';
|
||||||
@ -153,6 +152,7 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
|
|||||||
const mlModelTags = useMemo(() => {
|
const mlModelTags = useMemo(() => {
|
||||||
return getTagsWithoutTier(mlModelDetail.tags || []);
|
return getTagsWithoutTier(mlModelDetail.tags || []);
|
||||||
}, [mlModelDetail.tags]);
|
}, [mlModelDetail.tags]);
|
||||||
|
|
||||||
const slashedMlModelName: TitleBreadcrumbProps['titleLinks'] = [
|
const slashedMlModelName: TitleBreadcrumbProps['titleLinks'] = [
|
||||||
{
|
{
|
||||||
name: mlModelDetail.service.name || '',
|
name: mlModelDetail.service.name || '',
|
||||||
@ -162,14 +162,6 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
|
|||||||
ServiceCategory.ML_MODEL_SERVICES
|
ServiceCategory.ML_MODEL_SERVICES
|
||||||
)
|
)
|
||||||
: '',
|
: '',
|
||||||
imgSrc: mlModelDetail.serviceType
|
|
||||||
? serviceTypeLogo(mlModelDetail.serviceType || '')
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: getEntityName(mlModelDetail as unknown as EntityReference),
|
|
||||||
url: '',
|
|
||||||
activeTitle: true,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -553,6 +545,7 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
|
|||||||
? onTierRemove
|
? onTierRemove
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
serviceType={mlModelDetail.serviceType ?? ''}
|
||||||
tags={mlModelTags}
|
tags={mlModelTags}
|
||||||
tagsHandler={onTagUpdate}
|
tagsHandler={onTagUpdate}
|
||||||
tier={mlModelTier}
|
tier={mlModelTier}
|
||||||
|
@ -273,6 +273,7 @@ const MlModelVersion: FC<MlModelVersionProp> = ({
|
|||||||
}
|
}
|
||||||
extraInfo={getExtraInfo()}
|
extraInfo={getExtraInfo()}
|
||||||
followersList={[]}
|
followersList={[]}
|
||||||
|
serviceType={currentVersionData.serviceType ?? ''}
|
||||||
tags={getTags()}
|
tags={getTags()}
|
||||||
tier={{} as TagLabel}
|
tier={{} as TagLabel}
|
||||||
titleLinks={slashedMlModelName}
|
titleLinks={slashedMlModelName}
|
||||||
|
@ -160,14 +160,6 @@ const mockProp: MyDataProps = {
|
|||||||
updateThreadHandler: jest.fn(),
|
updateThreadHandler: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockObserve = jest.fn();
|
|
||||||
const mockunObserve = jest.fn();
|
|
||||||
|
|
||||||
window.IntersectionObserver = jest.fn().mockImplementation(() => ({
|
|
||||||
observe: mockObserve,
|
|
||||||
unobserve: mockunObserve,
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('Test MyData page', () => {
|
describe('Test MyData page', () => {
|
||||||
it('Check if there is an element in the page', async () => {
|
it('Check if there is an element in the page', async () => {
|
||||||
const { container } = render(<MyData {...mockProp} />, {
|
const { container } = render(<MyData {...mockProp} />, {
|
||||||
@ -197,8 +189,6 @@ describe('Test MyData page', () => {
|
|||||||
const obServerElement = await findByTestId(container, 'observer-element');
|
const obServerElement = await findByTestId(container, 'observer-element');
|
||||||
|
|
||||||
expect(obServerElement).toBeInTheDocument();
|
expect(obServerElement).toBeInTheDocument();
|
||||||
|
|
||||||
expect(mockObserve).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Onboarding placeholder should be visible in case of no activity feed present overall and the feeds are not loading', async () => {
|
it('Onboarding placeholder should be visible in case of no activity feed present overall and the feeds are not loading', async () => {
|
||||||
|
@ -763,6 +763,7 @@ const PipelineDetails = ({
|
|||||||
? onTierRemove
|
? onTierRemove
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
serviceType={pipelineDetails.serviceType ?? ''}
|
||||||
tags={tags}
|
tags={tags}
|
||||||
tagsHandler={onTagUpdate}
|
tagsHandler={onTagUpdate}
|
||||||
tier={tier}
|
tier={tier}
|
||||||
|
@ -20,7 +20,6 @@ import {
|
|||||||
screen,
|
screen,
|
||||||
} from '@testing-library/react';
|
} from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { act } from 'react-test-renderer';
|
import { act } from 'react-test-renderer';
|
||||||
@ -141,14 +140,6 @@ const PipelineDetailsProps = {
|
|||||||
onExtensionUpdate: jest.fn(),
|
onExtensionUpdate: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockObserve = jest.fn();
|
|
||||||
const mockunObserve = jest.fn();
|
|
||||||
|
|
||||||
window.IntersectionObserver = jest.fn().mockImplementation(() => ({
|
|
||||||
observe: mockObserve,
|
|
||||||
unobserve: mockunObserve,
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('../common/description/Description', () => {
|
jest.mock('../common/description/Description', () => {
|
||||||
return jest.fn().mockReturnValue(<p>Description Component</p>);
|
return jest.fn().mockReturnValue(<p>Description Component</p>);
|
||||||
});
|
});
|
||||||
|
@ -278,6 +278,7 @@ const PipelineVersion: FC<PipelineVersionProp> = ({
|
|||||||
}
|
}
|
||||||
extraInfo={getExtraInfo()}
|
extraInfo={getExtraInfo()}
|
||||||
followersList={[]}
|
followersList={[]}
|
||||||
|
serviceType={currentVersionData.serviceType ?? ''}
|
||||||
tags={getTags()}
|
tags={getTags()}
|
||||||
tier={{} as TagLabel}
|
tier={{} as TagLabel}
|
||||||
titleLinks={slashedPipelineName}
|
titleLinks={slashedPipelineName}
|
||||||
|
@ -466,6 +466,7 @@ const ProfilerDashboard: React.FC<ProfilerDashboardProps> = ({
|
|||||||
? handleTierRemove
|
? handleTierRemove
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
serviceType={table.serviceType ?? ''}
|
||||||
tags={getTagsWithoutTier(table.tags || [])}
|
tags={getTagsWithoutTier(table.tags || [])}
|
||||||
tagsHandler={handleTagUpdate}
|
tagsHandler={handleTagUpdate}
|
||||||
tier={tier}
|
tier={tier}
|
||||||
|
@ -892,12 +892,11 @@ const TeamDetailsV1 = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="table-container">
|
<div data-testid="table-container">
|
||||||
{assets.data.map(({ _source, _index, _id = '' }, index) => (
|
{assets.data.map(({ _source, _id = '' }, index) => (
|
||||||
<TableDataCardV2
|
<TableDataCardV2
|
||||||
className="m-b-sm cursor-pointer"
|
className="m-b-sm cursor-pointer"
|
||||||
id={_id}
|
id={_id}
|
||||||
key={index}
|
key={index}
|
||||||
searchIndex={_index as SearchIndex}
|
|
||||||
source={_source}
|
source={_source}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
@ -452,6 +452,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
|
|||||||
? onTierRemove
|
? onTierRemove
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
serviceType={topicDetails.serviceType ?? ''}
|
||||||
tags={topicTags}
|
tags={topicTags}
|
||||||
tagsHandler={onTagUpdate}
|
tagsHandler={onTagUpdate}
|
||||||
tier={tier}
|
tier={tier}
|
||||||
|
@ -109,14 +109,6 @@ const TopicDetailsProps = {
|
|||||||
onExtensionUpdate: jest.fn(),
|
onExtensionUpdate: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockObserve = jest.fn();
|
|
||||||
const mockunObserve = jest.fn();
|
|
||||||
|
|
||||||
window.IntersectionObserver = jest.fn().mockImplementation(() => ({
|
|
||||||
observe: mockObserve,
|
|
||||||
unobserve: mockunObserve,
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('../EntityLineage/EntityLineage.component', () => {
|
jest.mock('../EntityLineage/EntityLineage.component', () => {
|
||||||
return jest.fn().mockReturnValue(<p>EntityLineage.component</p>);
|
return jest.fn().mockReturnValue(<p>EntityLineage.component</p>);
|
||||||
});
|
});
|
||||||
@ -285,7 +277,5 @@ describe('Test TopicDetails component', () => {
|
|||||||
const obServerElement = await findByTestId(container, 'observer-element');
|
const obServerElement = await findByTestId(container, 'observer-element');
|
||||||
|
|
||||||
expect(obServerElement).toBeInTheDocument();
|
expect(obServerElement).toBeInTheDocument();
|
||||||
|
|
||||||
expect(mockObserve).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -273,6 +273,7 @@ const TopicVersion: FC<TopicVersionProp> = ({
|
|||||||
entityName={currentVersionData.name ?? ''}
|
entityName={currentVersionData.name ?? ''}
|
||||||
extraInfo={getExtraInfo()}
|
extraInfo={getExtraInfo()}
|
||||||
followersList={[]}
|
followersList={[]}
|
||||||
|
serviceType={currentVersionData.serviceType ?? ''}
|
||||||
tags={getTags()}
|
tags={getTags()}
|
||||||
tier={{} as TagLabel}
|
tier={{} as TagLabel}
|
||||||
titleLinks={slashedTopicName}
|
titleLinks={slashedTopicName}
|
||||||
|
@ -25,7 +25,6 @@ import { ReactComponent as IconTeamsGrey } from 'assets/svg/teams-grey.svg';
|
|||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import TableDataCardV2 from 'components/common/table-data-card-v2/TableDataCardV2';
|
import TableDataCardV2 from 'components/common/table-data-card-v2/TableDataCardV2';
|
||||||
import TeamsSelectable from 'components/TeamsSelectable/TeamsSelectable';
|
import TeamsSelectable from 'components/TeamsSelectable/TeamsSelectable';
|
||||||
import { SearchIndex } from 'enums/search.enum';
|
|
||||||
import { capitalize, isEmpty, isEqual, toLower } from 'lodash';
|
import { capitalize, isEmpty, isEqual, toLower } from 'lodash';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import React, {
|
import React, {
|
||||||
@ -848,12 +847,11 @@ const Users = ({
|
|||||||
<div data-testid="table-container">
|
<div data-testid="table-container">
|
||||||
{entityData.data.length ? (
|
{entityData.data.length ? (
|
||||||
<>
|
<>
|
||||||
{entityData.data.map(({ _source, _index, _id = '' }, index) => (
|
{entityData.data.map(({ _source, _id = '' }, index) => (
|
||||||
<TableDataCardV2
|
<TableDataCardV2
|
||||||
className="m-b-sm cursor-pointer"
|
className="m-b-sm cursor-pointer"
|
||||||
id={_id}
|
id={_id}
|
||||||
key={index}
|
key={index}
|
||||||
searchIndex={_index as SearchIndex}
|
|
||||||
source={_source}
|
source={_source}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
@ -114,9 +114,11 @@ const mockEntityInfoProp = {
|
|||||||
versionHandler,
|
versionHandler,
|
||||||
entityFieldThreads: [],
|
entityFieldThreads: [],
|
||||||
onThreadLinkSelect,
|
onThreadLinkSelect,
|
||||||
|
serviceType: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.mock('../../../utils/EntityUtils', () => ({
|
jest.mock('../../../utils/EntityUtils', () => ({
|
||||||
|
...jest.requireActual('../../../utils/EntityUtils'),
|
||||||
getEntityFeedLink: jest.fn(),
|
getEntityFeedLink: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -11,11 +11,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ExclamationCircleOutlined, StarFilled } from '@ant-design/icons';
|
import { StarFilled } from '@ant-design/icons';
|
||||||
import { Button, Popover, Space, Tooltip } from 'antd';
|
import { Button, Popover, Space, Tooltip } from 'antd';
|
||||||
import { ItemType } from 'antd/lib/menu/hooks/useItems';
|
import { ItemType } from 'antd/lib/menu/hooks/useItems';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { EntityHeader } from 'components/Entity/EntityHeader/EntityHeader.component';
|
||||||
import Tags from 'components/Tag/Tags/tags';
|
import Tags from 'components/Tag/Tags/tags';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { cloneDeep, isEmpty, isUndefined, toString } from 'lodash';
|
import { cloneDeep, isEmpty, isUndefined, toString } from 'lodash';
|
||||||
@ -23,6 +24,7 @@ import { EntityTags, ExtraInfo, TagOption } from 'Models';
|
|||||||
import React, { Fragment, useCallback, useEffect, useState } from 'react';
|
import React, { Fragment, useCallback, useEffect, useState } from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { getActiveAnnouncement } from 'rest/feedsAPI';
|
import { getActiveAnnouncement } from 'rest/feedsAPI';
|
||||||
|
import { serviceTypeLogo } from 'utils/ServiceUtils';
|
||||||
import { ReactComponent as IconCommentPlus } from '../../../assets/svg/add-chat.svg';
|
import { ReactComponent as IconCommentPlus } from '../../../assets/svg/add-chat.svg';
|
||||||
import { ReactComponent as IconComments } from '../../../assets/svg/comment.svg';
|
import { ReactComponent as IconComments } from '../../../assets/svg/comment.svg';
|
||||||
import { ReactComponent as IconEdit } from '../../../assets/svg/ic-edit.svg';
|
import { ReactComponent as IconEdit } from '../../../assets/svg/ic-edit.svg';
|
||||||
@ -53,7 +55,6 @@ import TagsContainer from '../../Tag/TagsContainer/tags-container';
|
|||||||
import TagsViewer from '../../Tag/TagsViewer/tags-viewer';
|
import TagsViewer from '../../Tag/TagsViewer/tags-viewer';
|
||||||
import EntitySummaryDetails from '../EntitySummaryDetails/EntitySummaryDetails';
|
import EntitySummaryDetails from '../EntitySummaryDetails/EntitySummaryDetails';
|
||||||
import ProfilePicture from '../ProfilePicture/ProfilePicture';
|
import ProfilePicture from '../ProfilePicture/ProfilePicture';
|
||||||
import TitleBreadcrumb from '../title-breadcrumb/title-breadcrumb.component';
|
|
||||||
import { TitleBreadcrumbProps } from '../title-breadcrumb/title-breadcrumb.interface';
|
import { TitleBreadcrumbProps } from '../title-breadcrumb/title-breadcrumb.interface';
|
||||||
import AnnouncementCard from './AnnouncementCard/AnnouncementCard';
|
import AnnouncementCard from './AnnouncementCard/AnnouncementCard';
|
||||||
import AnnouncementDrawer from './AnnouncementDrawer/AnnouncementDrawer';
|
import AnnouncementDrawer from './AnnouncementDrawer/AnnouncementDrawer';
|
||||||
@ -90,6 +91,7 @@ interface Props {
|
|||||||
onRestoreEntity?: () => void;
|
onRestoreEntity?: () => void;
|
||||||
isRecursiveDelete?: boolean;
|
isRecursiveDelete?: boolean;
|
||||||
extraDropdownContent?: ItemType[];
|
extraDropdownContent?: ItemType[];
|
||||||
|
serviceType: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EntityPageInfo = ({
|
const EntityPageInfo = ({
|
||||||
@ -122,6 +124,7 @@ const EntityPageInfo = ({
|
|||||||
onRestoreEntity,
|
onRestoreEntity,
|
||||||
isRecursiveDelete = false,
|
isRecursiveDelete = false,
|
||||||
extraDropdownContent,
|
extraDropdownContent,
|
||||||
|
serviceType,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const tagThread = entityFieldThreads?.[0];
|
const tagThread = entityFieldThreads?.[0];
|
||||||
@ -132,9 +135,6 @@ const EntityPageInfo = ({
|
|||||||
const [isViewMore, setIsViewMore] = useState<boolean>(false);
|
const [isViewMore, setIsViewMore] = useState<boolean>(false);
|
||||||
const [tagList, setTagList] = useState<Array<TagOption>>([]);
|
const [tagList, setTagList] = useState<Array<TagOption>>([]);
|
||||||
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
||||||
const [versionFollowButtonWidth, setVersionFollowButtonWidth] = useState(
|
|
||||||
document.getElementById('version-and-follow-section')?.offsetWidth
|
|
||||||
);
|
|
||||||
|
|
||||||
const [isAnnouncementDrawerOpen, setIsAnnouncementDrawer] =
|
const [isAnnouncementDrawerOpen, setIsAnnouncementDrawer] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
@ -376,101 +376,98 @@ const EntityPageInfo = ({
|
|||||||
}, [followersList]);
|
}, [followersList]);
|
||||||
|
|
||||||
useAfterMount(() => {
|
useAfterMount(() => {
|
||||||
setVersionFollowButtonWidth(
|
|
||||||
document.getElementById('version-and-follow-section')?.offsetWidth
|
|
||||||
);
|
|
||||||
if (ANNOUNCEMENT_ENTITIES.includes(entityType as EntityType)) {
|
if (ANNOUNCEMENT_ENTITIES.includes(entityType as EntityType)) {
|
||||||
fetchActiveAnnouncement();
|
fetchActiveAnnouncement();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="entity-page-info">
|
<Space
|
||||||
<Space
|
className="w-full"
|
||||||
align="start"
|
data-testid="entity-page-info"
|
||||||
className="tw-justify-between"
|
direction="vertical">
|
||||||
style={{ width: '100%' }}>
|
<EntityHeader
|
||||||
<Space align="center">
|
breadcrumb={titleLinks}
|
||||||
<TitleBreadcrumb
|
entityData={{
|
||||||
titleLinks={titleLinks}
|
displayName: entityName,
|
||||||
widthDeductions={
|
name: entityName,
|
||||||
(versionFollowButtonWidth ? versionFollowButtonWidth : 0) + 30
|
deleted,
|
||||||
}
|
}}
|
||||||
/>
|
entityType={(entityType as EntityType) ?? EntityType.TABLE}
|
||||||
{deleted && (
|
extra={
|
||||||
<div className="deleted-badge-button" data-testid="deleted-badge">
|
<Space align="center" id="version-and-follow-section">
|
||||||
<ExclamationCircleOutlined className="tw-mr-1" />
|
{!isUndefined(version) ? (
|
||||||
{t('label.deleted')}
|
<>
|
||||||
</div>
|
{!isUndefined(isVersionSelected) ? (
|
||||||
)}
|
<Tooltip
|
||||||
</Space>
|
placement="bottom"
|
||||||
<Space align="center" id="version-and-follow-section">
|
title={
|
||||||
{!isUndefined(version) ? (
|
<p className="tw-text-xs">
|
||||||
<>
|
{t('message.viewing-older-version')}
|
||||||
{!isUndefined(isVersionSelected) ? (
|
</p>
|
||||||
<Tooltip
|
}
|
||||||
placement="bottom"
|
trigger="hover">
|
||||||
title={
|
{getVersionButton(toString(version))}
|
||||||
<p className="tw-text-xs">
|
</Tooltip>
|
||||||
{t('message.viewing-older-version')}
|
) : (
|
||||||
</p>
|
<>{getVersionButton(toString(version))}</>
|
||||||
}
|
)}
|
||||||
trigger="hover">
|
</>
|
||||||
{getVersionButton(toString(version))}
|
) : null}
|
||||||
</Tooltip>
|
{!isUndefined(isFollowing) ? (
|
||||||
) : (
|
<Button
|
||||||
<>{getVersionButton(toString(version))}</>
|
className={classNames(
|
||||||
)}
|
'tw-border tw-border-primary tw-rounded',
|
||||||
</>
|
isFollowing ? 'tw-text-white' : 'tw-text-primary'
|
||||||
) : null}
|
)}
|
||||||
{!isUndefined(isFollowing) ? (
|
data-testid="follow-button"
|
||||||
<Button
|
size="small"
|
||||||
className={classNames(
|
type={isFollowing ? 'primary' : 'default'}
|
||||||
'tw-border tw-border-primary tw-rounded',
|
onClick={() => {
|
||||||
isFollowing ? 'tw-text-white' : 'tw-text-primary'
|
!deleted && followHandler?.();
|
||||||
)}
|
}}>
|
||||||
data-testid="follow-button"
|
<Space>
|
||||||
size="small"
|
<StarFilled className="tw-text-xs" />
|
||||||
type={isFollowing ? 'primary' : 'default'}
|
{isFollowing ? t('label.un-follow') : t('label.follow')}
|
||||||
onClick={() => {
|
<Popover content={getFollowers()} trigger="click">
|
||||||
!deleted && followHandler?.();
|
<span
|
||||||
}}>
|
className={classNames(
|
||||||
<Space>
|
'tw-border-l tw-font-medium tw-cursor-pointer hover:tw-underline tw-pl-1',
|
||||||
<StarFilled className="tw-text-xs" />
|
{ 'tw-border-primary': !isFollowing }
|
||||||
{isFollowing ? t('label.un-follow') : t('label.follow')}
|
)}
|
||||||
<Popover content={getFollowers()} trigger="click">
|
data-testid="follower-value"
|
||||||
<span
|
onClick={(e) => e.stopPropagation()}>
|
||||||
className={classNames(
|
{followers}
|
||||||
'tw-border-l tw-font-medium tw-cursor-pointer hover:tw-underline tw-pl-1',
|
</span>
|
||||||
{ 'tw-border-primary': !isFollowing }
|
</Popover>
|
||||||
)}
|
</Space>
|
||||||
data-testid="follower-value"
|
</Button>
|
||||||
onClick={(e) => e.stopPropagation()}>
|
) : null}
|
||||||
{followers}
|
{!isVersionSelected && (
|
||||||
</span>
|
<ManageButton
|
||||||
</Popover>
|
allowSoftDelete={!deleted}
|
||||||
</Space>
|
canDelete={canDelete}
|
||||||
</Button>
|
deleted={deleted}
|
||||||
) : null}
|
entityFQN={entityFqn}
|
||||||
{!isVersionSelected && (
|
entityId={entityId}
|
||||||
<ManageButton
|
entityName={entityName}
|
||||||
allowSoftDelete={!deleted}
|
entityType={entityType}
|
||||||
canDelete={canDelete}
|
extraDropdownContent={extraDropdownContent}
|
||||||
deleted={deleted}
|
isRecursiveDelete={isRecursiveDelete}
|
||||||
entityFQN={entityFqn}
|
onAnnouncementClick={() => setIsAnnouncementDrawer(true)}
|
||||||
entityId={entityId}
|
onRestoreEntity={onRestoreEntity}
|
||||||
entityName={entityName}
|
/>
|
||||||
entityType={entityType}
|
)}
|
||||||
extraDropdownContent={extraDropdownContent}
|
</Space>
|
||||||
isRecursiveDelete={isRecursiveDelete}
|
}
|
||||||
onAnnouncementClick={() => setIsAnnouncementDrawer(true)}
|
icon={
|
||||||
onRestoreEntity={onRestoreEntity}
|
serviceType && (
|
||||||
/>
|
<img className="h-8" src={serviceTypeLogo(serviceType)} />
|
||||||
)}
|
)
|
||||||
</Space>
|
}
|
||||||
</Space>
|
/>
|
||||||
|
|
||||||
<Space wrap className="tw-justify-between" style={{ width: '100%' }}>
|
<Space wrap className="justify-between w-full" size={16}>
|
||||||
<Space direction="vertical">
|
<Space direction="vertical">
|
||||||
<Space wrap align="center" data-testid="extrainfo" size={4}>
|
<Space wrap align="center" data-testid="extrainfo" size={4}>
|
||||||
{extraInfo.map((info, index) => (
|
{extraInfo.map((info, index) => (
|
||||||
@ -499,9 +496,7 @@ const EntityPageInfo = ({
|
|||||||
{(!isEditable || !isTagEditable || deleted) && (
|
{(!isEditable || !isTagEditable || deleted) && (
|
||||||
<>
|
<>
|
||||||
{(tags.length > 0 || !isEmpty(tier)) && (
|
{(tags.length > 0 || !isEmpty(tier)) && (
|
||||||
<span className="d-flex align-center h-4">
|
<IconTagGrey height={14} name="icon-tag" />
|
||||||
<IconTagGrey height={18} name="icon-tag" width={18} />
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
{tier?.tagFQN && (
|
{tier?.tagFQN && (
|
||||||
<Tags
|
<Tags
|
||||||
@ -603,7 +598,7 @@ const EntityPageInfo = ({
|
|||||||
onClose={() => setIsAnnouncementDrawer(false)}
|
onClose={() => setIsAnnouncementDrawer(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Space>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,110 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 Collate.
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Button, Typography } from 'antd';
|
|
||||||
import { ReactComponent as IconExternalLink } from 'assets/svg/external-link.svg';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { toString } from 'lodash';
|
|
||||||
import React, { useMemo } from 'react';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { ROUTES } from '../../../constants/constants';
|
|
||||||
import { EntityType, FqnPart } from '../../../enums/entity.enum';
|
|
||||||
import { SearchIndex } from '../../../enums/search.enum';
|
|
||||||
import {
|
|
||||||
getNameFromFQN,
|
|
||||||
getPartialNameFromTableFQN,
|
|
||||||
} from '../../../utils/CommonUtils';
|
|
||||||
import { stringToHTML } from '../../../utils/StringsUtils';
|
|
||||||
import { getEntityLink } from '../../../utils/TableUtils';
|
|
||||||
import './TableDataCardTitle.less';
|
|
||||||
|
|
||||||
interface TableDataCardTitleProps {
|
|
||||||
dataTestId?: string;
|
|
||||||
id?: string;
|
|
||||||
searchIndex: SearchIndex | EntityType;
|
|
||||||
source: {
|
|
||||||
fullyQualifiedName?: string;
|
|
||||||
displayName?: string;
|
|
||||||
name?: string;
|
|
||||||
type?: string;
|
|
||||||
};
|
|
||||||
isPanel?: boolean;
|
|
||||||
handleLinkClick?: (e: React.MouseEvent) => void;
|
|
||||||
openEntityInNewPage?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TableDataCardTitle = ({
|
|
||||||
dataTestId,
|
|
||||||
id,
|
|
||||||
searchIndex,
|
|
||||||
source,
|
|
||||||
handleLinkClick,
|
|
||||||
isPanel = false,
|
|
||||||
openEntityInNewPage = false,
|
|
||||||
}: TableDataCardTitleProps) => {
|
|
||||||
const isTourRoute = location.pathname.includes(ROUTES.TOUR);
|
|
||||||
|
|
||||||
const { testId, displayName } = useMemo(
|
|
||||||
() => ({
|
|
||||||
testId: dataTestId
|
|
||||||
? dataTestId
|
|
||||||
: `${getPartialNameFromTableFQN(source.fullyQualifiedName ?? '', [
|
|
||||||
FqnPart.Service,
|
|
||||||
])}-${source.name}`,
|
|
||||||
displayName:
|
|
||||||
source.type === 'tag'
|
|
||||||
? toString(getNameFromFQN(source.fullyQualifiedName ?? ''))
|
|
||||||
: toString(source.displayName),
|
|
||||||
}),
|
|
||||||
[dataTestId, source]
|
|
||||||
);
|
|
||||||
|
|
||||||
const title = (
|
|
||||||
<Button
|
|
||||||
data-testid={testId}
|
|
||||||
id={`${id ?? testId}-title`}
|
|
||||||
type="link"
|
|
||||||
onClick={isTourRoute ? handleLinkClick : undefined}>
|
|
||||||
{stringToHTML(displayName)}
|
|
||||||
{openEntityInNewPage && (
|
|
||||||
<IconExternalLink className="anticon" height={14} />
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isTourRoute) {
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Typography.Title
|
|
||||||
ellipsis
|
|
||||||
className="m-b-0 text-base"
|
|
||||||
level={5}
|
|
||||||
title={displayName}>
|
|
||||||
<Link
|
|
||||||
className={classNames(
|
|
||||||
'table-data-card-title-container w-fit-content w-max-90',
|
|
||||||
{
|
|
||||||
'button-hover': isPanel,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
target={openEntityInNewPage ? '_blank' : '_self'}
|
|
||||||
to={getEntityLink(searchIndex, source.fullyQualifiedName ?? '')}>
|
|
||||||
{title}
|
|
||||||
</Link>
|
|
||||||
</Typography.Title>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TableDataCardTitle;
|
|
@ -14,7 +14,6 @@
|
|||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { SearchIndex } from '../../../enums/search.enum';
|
|
||||||
import TableDataCardV2 from './TableDataCardV2';
|
import TableDataCardV2 from './TableDataCardV2';
|
||||||
|
|
||||||
jest.mock('../../../utils/TableUtils', () => ({
|
jest.mock('../../../utils/TableUtils', () => ({
|
||||||
@ -42,13 +41,16 @@ jest.mock('../table-data-card/TableDataCardBody', () => {
|
|||||||
|
|
||||||
const mockHandleSummaryPanelDisplay = jest.fn();
|
const mockHandleSummaryPanelDisplay = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('components/Entity/EntityHeader/EntityHeader.component', () => ({
|
||||||
|
EntityHeader: jest.fn().mockImplementation(() => <p>EntityHeader</p>),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('Test TableDataCard Component', () => {
|
describe('Test TableDataCard Component', () => {
|
||||||
it('Component should render', () => {
|
it('Component should render', () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId, getByText } = render(
|
||||||
<TableDataCardV2
|
<TableDataCardV2
|
||||||
handleSummaryPanelDisplay={mockHandleSummaryPanelDisplay}
|
handleSummaryPanelDisplay={mockHandleSummaryPanelDisplay}
|
||||||
id="1"
|
id="1"
|
||||||
searchIndex={SearchIndex.TABLE}
|
|
||||||
source={{
|
source={{
|
||||||
id: '1',
|
id: '1',
|
||||||
name: 'Name1',
|
name: 'Name1',
|
||||||
@ -57,26 +59,9 @@ describe('Test TableDataCard Component', () => {
|
|||||||
{ wrapper: MemoryRouter }
|
{ wrapper: MemoryRouter }
|
||||||
);
|
);
|
||||||
const tableDataCard = getByTestId('table-data-card');
|
const tableDataCard = getByTestId('table-data-card');
|
||||||
|
const entityHeader = getByText('EntityHeader');
|
||||||
|
|
||||||
expect(tableDataCard).toBeInTheDocument();
|
expect(tableDataCard).toBeInTheDocument();
|
||||||
});
|
expect(entityHeader).toBeInTheDocument();
|
||||||
|
|
||||||
it('Component should render for deleted', () => {
|
|
||||||
const { getByTestId } = render(
|
|
||||||
<TableDataCardV2
|
|
||||||
handleSummaryPanelDisplay={mockHandleSummaryPanelDisplay}
|
|
||||||
id="1"
|
|
||||||
searchIndex={SearchIndex.TABLE}
|
|
||||||
source={{
|
|
||||||
id: '2',
|
|
||||||
name: 'Name2',
|
|
||||||
deleted: true,
|
|
||||||
}}
|
|
||||||
/>,
|
|
||||||
{ wrapper: MemoryRouter }
|
|
||||||
);
|
|
||||||
const deleted = getByTestId('deleted');
|
|
||||||
|
|
||||||
expect(deleted).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -11,36 +11,31 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ExclamationCircleOutlined } from '@ant-design/icons';
|
|
||||||
import { Checkbox } from 'antd';
|
import { Checkbox } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { EntityHeader } from 'components/Entity/EntityHeader/EntityHeader.component';
|
||||||
import { isString, startCase, uniqueId } from 'lodash';
|
import { isString, startCase, uniqueId } from 'lodash';
|
||||||
import { ExtraInfo } from 'Models';
|
import { ExtraInfo } from 'Models';
|
||||||
import React, { forwardRef, useMemo } from 'react';
|
import React, { forwardRef, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useLocation, useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { getEntityId, getEntityName } from 'utils/EntityUtils';
|
import {
|
||||||
import AppState from '../../../AppState';
|
getEntityBreadcrumbs,
|
||||||
|
getEntityId,
|
||||||
|
getEntityName,
|
||||||
|
} from 'utils/EntityUtils';
|
||||||
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
|
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
|
||||||
import { ROUTES } from '../../../constants/constants';
|
|
||||||
import { EntityType } from '../../../enums/entity.enum';
|
import { EntityType } from '../../../enums/entity.enum';
|
||||||
import { SearchIndex } from '../../../enums/search.enum';
|
|
||||||
import { CurrentTourPageType } from '../../../enums/tour.enum';
|
|
||||||
import { OwnerType } from '../../../enums/user.enum';
|
import { OwnerType } from '../../../enums/user.enum';
|
||||||
import { EntityReference } from '../../../generated/entity/type';
|
import { EntityReference } from '../../../generated/entity/type';
|
||||||
import {
|
import {
|
||||||
getEntityPlaceHolder,
|
getEntityPlaceHolder,
|
||||||
getOwnerValue,
|
getOwnerValue,
|
||||||
} from '../../../utils/CommonUtils';
|
} from '../../../utils/CommonUtils';
|
||||||
import {
|
import { getServiceIcon, getUsagePercentile } from '../../../utils/TableUtils';
|
||||||
getEntityHeaderLabel,
|
|
||||||
getServiceIcon,
|
|
||||||
getUsagePercentile,
|
|
||||||
} from '../../../utils/TableUtils';
|
|
||||||
import { SearchedDataProps } from '../../searched-data/SearchedData.interface';
|
import { SearchedDataProps } from '../../searched-data/SearchedData.interface';
|
||||||
import '../table-data-card/TableDataCard.style.css';
|
import '../table-data-card/TableDataCard.style.css';
|
||||||
import TableDataCardBody from '../table-data-card/TableDataCardBody';
|
import TableDataCardBody from '../table-data-card/TableDataCardBody';
|
||||||
import TableDataCardTitle from './TableDataCardTitle.component';
|
|
||||||
import './TableDataCardV2.less';
|
import './TableDataCardV2.less';
|
||||||
|
|
||||||
export interface TableDataCardPropsV2 {
|
export interface TableDataCardPropsV2 {
|
||||||
@ -51,7 +46,6 @@ export interface TableDataCardPropsV2 {
|
|||||||
key: string;
|
key: string;
|
||||||
value: number;
|
value: number;
|
||||||
}[];
|
}[];
|
||||||
searchIndex: SearchIndex | EntityType;
|
|
||||||
handleSummaryPanelDisplay?: (
|
handleSummaryPanelDisplay?: (
|
||||||
details: SearchedDataProps['data'][number]['_source'],
|
details: SearchedDataProps['data'][number]['_source'],
|
||||||
entityType: string
|
entityType: string
|
||||||
@ -71,16 +65,14 @@ const TableDataCardV2: React.FC<TableDataCardPropsV2> = forwardRef<
|
|||||||
className,
|
className,
|
||||||
source,
|
source,
|
||||||
matches,
|
matches,
|
||||||
searchIndex,
|
|
||||||
handleSummaryPanelDisplay,
|
handleSummaryPanelDisplay,
|
||||||
showCheckboxes,
|
showCheckboxes,
|
||||||
checked,
|
checked,
|
||||||
openEntityInNewPage = false,
|
openEntityInNewPage,
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const location = useLocation();
|
|
||||||
const { tab } = useParams<{ tab: string }>();
|
const { tab } = useParams<{ tab: string }>();
|
||||||
|
|
||||||
const otherDetails = useMemo(() => {
|
const otherDetails = useMemo(() => {
|
||||||
@ -137,21 +129,15 @@ const TableDataCardV2: React.FC<TableDataCardPropsV2> = forwardRef<
|
|||||||
return _otherDetails;
|
return _otherDetails;
|
||||||
}, [source]);
|
}, [source]);
|
||||||
|
|
||||||
const handleLinkClick = (e: React.MouseEvent) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
if (location.pathname.includes(ROUTES.TOUR)) {
|
|
||||||
AppState.currentTourPage = CurrentTourPageType.DATASET_PAGE;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const headerLabel = useMemo(() => {
|
|
||||||
return getEntityHeaderLabel(source);
|
|
||||||
}, [source]);
|
|
||||||
|
|
||||||
const serviceIcon = useMemo(() => {
|
const serviceIcon = useMemo(() => {
|
||||||
return getServiceIcon(source);
|
return getServiceIcon(source);
|
||||||
}, [source]);
|
}, [source]);
|
||||||
|
|
||||||
|
const breadcrumbs = useMemo(
|
||||||
|
() => getEntityBreadcrumbs(source, source.entityType as EntityType),
|
||||||
|
[source]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
@ -165,33 +151,20 @@ const TableDataCardV2: React.FC<TableDataCardPropsV2> = forwardRef<
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleSummaryPanelDisplay && handleSummaryPanelDisplay(source, tab);
|
handleSummaryPanelDisplay && handleSummaryPanelDisplay(source, tab);
|
||||||
}}>
|
}}>
|
||||||
<div>
|
<EntityHeader
|
||||||
{showCheckboxes && (
|
titleIsLink
|
||||||
<Checkbox checked={checked} className="float-right" />
|
breadcrumb={breadcrumbs}
|
||||||
)}
|
entityData={source}
|
||||||
{headerLabel}
|
entityType={source.entityType as EntityType}
|
||||||
<div className="tw-flex tw-items-center">
|
extra={
|
||||||
{serviceIcon}
|
showCheckboxes && (
|
||||||
<TableDataCardTitle
|
<Checkbox checked={checked} className="m-l-auto" />
|
||||||
handleLinkClick={handleLinkClick}
|
)
|
||||||
id={id}
|
}
|
||||||
openEntityInNewPage={openEntityInNewPage}
|
icon={serviceIcon}
|
||||||
searchIndex={searchIndex}
|
openEntityInNewPage={openEntityInNewPage}
|
||||||
source={source}
|
/>
|
||||||
/>
|
|
||||||
|
|
||||||
{source.deleted && (
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
className="tw-rounded tw-bg-error-lite tw-text-error tw-text-xs tw-font-medium tw-h-5 tw-px-1.5 tw-py-0.5 tw-ml-2"
|
|
||||||
data-testid="deleted">
|
|
||||||
<ExclamationCircleOutlined className="tw-mr-1" />
|
|
||||||
{t('label.deleted')}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="tw-pt-3">
|
<div className="tw-pt-3">
|
||||||
<TableDataCardBody
|
<TableDataCardBody
|
||||||
description={source.description || ''}
|
description={source.description || ''}
|
||||||
|
@ -13,9 +13,10 @@
|
|||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { ELASTICSEARCH_ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum';
|
import { ELASTICSEARCH_ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum';
|
||||||
import { isUndefined, toString } from 'lodash';
|
import { isUndefined } from 'lodash';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { getEntityName } from 'utils/EntityUtils';
|
||||||
import { PAGE_SIZE } from '../../constants/constants';
|
import { PAGE_SIZE } from '../../constants/constants';
|
||||||
import { MAX_RESULT_HITS } from '../../constants/explore.constants';
|
import { MAX_RESULT_HITS } from '../../constants/explore.constants';
|
||||||
import { Paging } from '../../generated/type/paging';
|
import { Paging } from '../../generated/type/paging';
|
||||||
@ -51,7 +52,7 @@ const SearchedData: React.FC<SearchedDataProps> = ({
|
|||||||
handleSummaryPanelDisplay,
|
handleSummaryPanelDisplay,
|
||||||
}) => {
|
}) => {
|
||||||
const highlightSearchResult = () => {
|
const highlightSearchResult = () => {
|
||||||
return data.map(({ _source: table, highlight, _index }, index) => {
|
return data.map(({ _source: table, highlight }, index) => {
|
||||||
let tDesc = table.description ?? '';
|
let tDesc = table.description ?? '';
|
||||||
const highLightedTexts = highlight?.description || [];
|
const highLightedTexts = highlight?.description || [];
|
||||||
|
|
||||||
@ -65,7 +66,7 @@ const SearchedData: React.FC<SearchedDataProps> = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = toString(table.displayName);
|
let name = getEntityName(table);
|
||||||
if (!isUndefined(highlight)) {
|
if (!isUndefined(highlight)) {
|
||||||
name = highlight?.name?.join(' ') || name;
|
name = highlight?.name?.join(' ') || name;
|
||||||
}
|
}
|
||||||
@ -102,7 +103,6 @@ const SearchedData: React.FC<SearchedDataProps> = ({
|
|||||||
handleSummaryPanelDisplay={handleSummaryPanelDisplay}
|
handleSummaryPanelDisplay={handleSummaryPanelDisplay}
|
||||||
id={`tabledatacard${index}`}
|
id={`tabledatacard${index}`}
|
||||||
matches={matches}
|
matches={matches}
|
||||||
searchIndex={_index}
|
|
||||||
source={{ ...table, name, description: tDesc }}
|
source={{ ...table, name, description: tDesc }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -282,6 +282,7 @@
|
|||||||
"enter-property-value": "Enter Property Value",
|
"enter-property-value": "Enter Property Value",
|
||||||
"enter-type-password": "Enter {{type}} Password",
|
"enter-type-password": "Enter {{type}} Password",
|
||||||
"entity-count": "{{entity}} Count",
|
"entity-count": "{{entity}} Count",
|
||||||
|
"entity-detail-plural": "{{entity}} details",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-index": "{{entity}} index",
|
"entity-index": "{{entity}} index",
|
||||||
"entity-name": "{{entity}} Name",
|
"entity-name": "{{entity}} Name",
|
||||||
|
@ -282,6 +282,7 @@
|
|||||||
"enter-property-value": "Ingrese el valor de la propiedad",
|
"enter-property-value": "Ingrese el valor de la propiedad",
|
||||||
"enter-type-password": "Ingrese la contraseña de {{type}}",
|
"enter-type-password": "Ingrese la contraseña de {{type}}",
|
||||||
"entity-count": "Cantidad de {{entity}}",
|
"entity-count": "Cantidad de {{entity}}",
|
||||||
|
"entity-detail-plural": "{{entity}} details",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-index": "Índice de {{entity}}",
|
"entity-index": "Índice de {{entity}}",
|
||||||
"entity-name": "Nombre de {{entity}}",
|
"entity-name": "Nombre de {{entity}}",
|
||||||
|
@ -282,6 +282,7 @@
|
|||||||
"enter-property-value": "Entrer une Valeur pour la Propriété",
|
"enter-property-value": "Entrer une Valeur pour la Propriété",
|
||||||
"enter-type-password": "Enter {{type}} Password",
|
"enter-type-password": "Enter {{type}} Password",
|
||||||
"entity-count": "{{entity}} Count",
|
"entity-count": "{{entity}} Count",
|
||||||
|
"entity-detail-plural": "{{entity}} details",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-index": "{{entity}} index",
|
"entity-index": "{{entity}} index",
|
||||||
"entity-name": "{{entity}} Name",
|
"entity-name": "{{entity}} Name",
|
||||||
|
@ -282,6 +282,7 @@
|
|||||||
"enter-property-value": "プロパティの値を入力",
|
"enter-property-value": "プロパティの値を入力",
|
||||||
"enter-type-password": "{{type}} のパスワードを入力",
|
"enter-type-password": "{{type}} のパスワードを入力",
|
||||||
"entity-count": "{{entity}}の数",
|
"entity-count": "{{entity}}の数",
|
||||||
|
"entity-detail-plural": "{{entity}} details",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-index": "{{entity}} インデックス",
|
"entity-index": "{{entity}} インデックス",
|
||||||
"entity-name": "{{entity}} 名",
|
"entity-name": "{{entity}} 名",
|
||||||
|
@ -282,6 +282,7 @@
|
|||||||
"enter-property-value": "Introduzir valor da propriedade",
|
"enter-property-value": "Introduzir valor da propriedade",
|
||||||
"enter-type-password": "Introduzir {{type}} da senha",
|
"enter-type-password": "Introduzir {{type}} da senha",
|
||||||
"entity-count": "{{entity}} contagem",
|
"entity-count": "{{entity}} contagem",
|
||||||
|
"entity-detail-plural": "{{entity}} details",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-index": "Indíce da {{entity}}",
|
"entity-index": "Indíce da {{entity}}",
|
||||||
"entity-name": "Nome da {{entity}}",
|
"entity-name": "Nome da {{entity}}",
|
||||||
|
@ -282,6 +282,7 @@
|
|||||||
"enter-property-value": "输入属性值",
|
"enter-property-value": "输入属性值",
|
||||||
"enter-type-password": "输入 {{type}} 密码",
|
"enter-type-password": "输入 {{type}} 密码",
|
||||||
"entity-count": "{{entity}} Count",
|
"entity-count": "{{entity}} Count",
|
||||||
|
"entity-detail-plural": "{{entity}} details",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-index": "{{entity}} index",
|
"entity-index": "{{entity}} index",
|
||||||
"entity-name": "{{entity}} Name",
|
"entity-name": "{{entity}} Name",
|
||||||
|
@ -72,7 +72,6 @@ import { getContainerDetailPath } from 'utils/ContainerDetailUtils';
|
|||||||
import { getEntityLineage, getEntityName } from 'utils/EntityUtils';
|
import { getEntityLineage, getEntityName } from 'utils/EntityUtils';
|
||||||
import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils';
|
import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils';
|
||||||
import { getLineageViewPath } from 'utils/RouterUtils';
|
import { getLineageViewPath } from 'utils/RouterUtils';
|
||||||
import { serviceTypeLogo } from 'utils/ServiceUtils';
|
|
||||||
import { bytesToSize } from 'utils/StringsUtils';
|
import { bytesToSize } from 'utils/StringsUtils';
|
||||||
import { getTagsWithoutTier, getTierTags } from 'utils/TableUtils';
|
import { getTagsWithoutTier, getTierTags } from 'utils/TableUtils';
|
||||||
import { showErrorToast, showSuccessToast } from 'utils/ToastUtils';
|
import { showErrorToast, showSuccessToast } from 'utils/ToastUtils';
|
||||||
@ -286,7 +285,6 @@ const ContainerPage = () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const breadcrumbTitles = useMemo(() => {
|
const breadcrumbTitles = useMemo(() => {
|
||||||
const serviceType = containerData?.serviceType;
|
|
||||||
const service = containerData?.service;
|
const service = containerData?.service;
|
||||||
const serviceName = service?.name;
|
const serviceName = service?.name;
|
||||||
|
|
||||||
@ -301,14 +299,8 @@ const ContainerPage = () => {
|
|||||||
url: serviceName
|
url: serviceName
|
||||||
? getServiceDetailsPath(serviceName, ServiceCategory.STORAGE_SERVICES)
|
? getServiceDetailsPath(serviceName, ServiceCategory.STORAGE_SERVICES)
|
||||||
: '',
|
: '',
|
||||||
imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined,
|
|
||||||
},
|
},
|
||||||
...parentContainerItems,
|
...parentContainerItems,
|
||||||
{
|
|
||||||
name: entityName,
|
|
||||||
url: '',
|
|
||||||
activeTitle: true,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
}, [containerData, containerName, entityName, parentContainers]);
|
}, [containerData, containerName, entityName, parentContainers]);
|
||||||
|
|
||||||
@ -632,6 +624,7 @@ const ContainerPage = () => {
|
|||||||
isFollowing={isUserFollowing}
|
isFollowing={isUserFollowing}
|
||||||
isTagEditable={hasEditTagsPermission}
|
isTagEditable={hasEditTagsPermission}
|
||||||
removeTier={hasEditTierPermission ? handleRemoveTier : undefined}
|
removeTier={hasEditTierPermission ? handleRemoveTier : undefined}
|
||||||
|
serviceType={containerData?.serviceType ?? ''}
|
||||||
tags={tags}
|
tags={tags}
|
||||||
tagsHandler={handleUpdateTags}
|
tagsHandler={handleUpdateTags}
|
||||||
tier={tier}
|
tier={tier}
|
||||||
|
@ -65,7 +65,6 @@ import {
|
|||||||
import { getEntityFeedLink, getEntityName } from '../../utils/EntityUtils';
|
import { getEntityFeedLink, getEntityName } from '../../utils/EntityUtils';
|
||||||
import { deletePost, updateThreadData } from '../../utils/FeedUtils';
|
import { deletePost, updateThreadData } from '../../utils/FeedUtils';
|
||||||
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
||||||
import { serviceTypeLogo } from '../../utils/ServiceUtils';
|
|
||||||
import { showErrorToast } from '../../utils/ToastUtils';
|
import { showErrorToast } from '../../utils/ToastUtils';
|
||||||
|
|
||||||
export type ChartType = {
|
export type ChartType = {
|
||||||
@ -242,12 +241,6 @@ const DashboardDetailsPage = () => {
|
|||||||
ServiceCategory.DASHBOARD_SERVICES
|
ServiceCategory.DASHBOARD_SERVICES
|
||||||
)
|
)
|
||||||
: '',
|
: '',
|
||||||
imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: getEntityName(res),
|
|
||||||
url: '',
|
|
||||||
activeTitle: true,
|
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichText
|
|||||||
import TabsPane from 'components/common/TabsPane/TabsPane';
|
import TabsPane from 'components/common/TabsPane/TabsPane';
|
||||||
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
|
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
|
||||||
import PageContainerV1 from 'components/containers/PageContainerV1';
|
import PageContainerV1 from 'components/containers/PageContainerV1';
|
||||||
|
import PageLayoutV1 from 'components/containers/PageLayoutV1';
|
||||||
import Loader from 'components/Loader/Loader';
|
import Loader from 'components/Loader/Loader';
|
||||||
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
||||||
import {
|
import {
|
||||||
@ -110,10 +111,7 @@ import {
|
|||||||
} from '../../utils/FeedUtils';
|
} from '../../utils/FeedUtils';
|
||||||
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
||||||
import { getSettingPath } from '../../utils/RouterUtils';
|
import { getSettingPath } from '../../utils/RouterUtils';
|
||||||
import {
|
import { getServiceRouteFromServiceType } from '../../utils/ServiceUtils';
|
||||||
getServiceRouteFromServiceType,
|
|
||||||
serviceTypeLogo,
|
|
||||||
} from '../../utils/ServiceUtils';
|
|
||||||
import { getErrorText } from '../../utils/StringsUtils';
|
import { getErrorText } from '../../utils/StringsUtils';
|
||||||
import {
|
import {
|
||||||
getEntityLink,
|
getEntityLink,
|
||||||
@ -283,7 +281,6 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
id = '',
|
id = '',
|
||||||
name,
|
name,
|
||||||
service,
|
service,
|
||||||
serviceType,
|
|
||||||
database,
|
database,
|
||||||
tags,
|
tags,
|
||||||
} = res;
|
} = res;
|
||||||
@ -312,7 +309,6 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
ServiceCategory.DATABASE_SERVICES
|
ServiceCategory.DATABASE_SERVICES
|
||||||
)
|
)
|
||||||
: '',
|
: '',
|
||||||
imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: getPartialNameFromTableFQN(
|
name: getPartialNameFromTableFQN(
|
||||||
@ -321,11 +317,6 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
),
|
),
|
||||||
url: getDatabaseDetailsPath(database.fullyQualifiedName ?? ''),
|
url: getDatabaseDetailsPath(database.fullyQualifiedName ?? ''),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: getEntityName(res),
|
|
||||||
url: '',
|
|
||||||
activeTitle: true,
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
throw jsonData['api-error-messages']['unexpected-server-response'];
|
throw jsonData['api-error-messages']['unexpected-server-response'];
|
||||||
@ -634,7 +625,7 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
|
|
||||||
const getSchemaTableList = () => {
|
const getSchemaTableList = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<Col span={24}>
|
||||||
<TableAntd
|
<TableAntd
|
||||||
bordered
|
bordered
|
||||||
className="table-shadow"
|
className="table-shadow"
|
||||||
@ -659,7 +650,7 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
totalCount={tableInstanceCount}
|
totalCount={tableInstanceCount}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</Col>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -785,10 +776,10 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
{databaseSchemaPermission.ViewAll ||
|
{databaseSchemaPermission.ViewAll ||
|
||||||
databaseSchemaPermission.ViewBasic ? (
|
databaseSchemaPermission.ViewBasic ? (
|
||||||
<PageContainerV1>
|
<PageContainerV1>
|
||||||
<Row
|
<PageLayoutV1
|
||||||
className="p-x-md p-t-lg"
|
pageTitle={t('label.entity-detail-plural', {
|
||||||
data-testid="page-container"
|
entity: getEntityName(databaseSchema),
|
||||||
gutter={[0, 12]}>
|
})}>
|
||||||
{IsSchemaDetailsLoading ? (
|
{IsSchemaDetailsLoading ? (
|
||||||
<Skeleton
|
<Skeleton
|
||||||
active
|
active
|
||||||
@ -820,6 +811,7 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
databaseSchemaPermission.EditAll ||
|
databaseSchemaPermission.EditAll ||
|
||||||
databaseSchemaPermission.EditTags
|
databaseSchemaPermission.EditTags
|
||||||
}
|
}
|
||||||
|
serviceType={databaseSchema?.serviceType ?? ''}
|
||||||
tags={tags}
|
tags={tags}
|
||||||
tagsHandler={onTagUpdate}
|
tagsHandler={onTagUpdate}
|
||||||
tier={tier}
|
tier={tier}
|
||||||
@ -834,27 +826,6 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
onThreadLinkSelect={onThreadLinkSelect}
|
onThreadLinkSelect={onThreadLinkSelect}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col data-testid="description-container" span={24}>
|
|
||||||
<Description
|
|
||||||
description={description}
|
|
||||||
entityFieldThreads={getEntityFieldThreadCounts(
|
|
||||||
EntityField.DESCRIPTION,
|
|
||||||
entityFieldThreadCount
|
|
||||||
)}
|
|
||||||
entityFqn={databaseSchemaFQN}
|
|
||||||
entityName={databaseSchemaName}
|
|
||||||
entityType={EntityType.DATABASE_SCHEMA}
|
|
||||||
hasEditAccess={
|
|
||||||
databaseSchemaPermission.EditDescription ||
|
|
||||||
databaseSchemaPermission.EditAll
|
|
||||||
}
|
|
||||||
isEdit={isEdit}
|
|
||||||
onCancel={onCancel}
|
|
||||||
onDescriptionEdit={onDescriptionEdit}
|
|
||||||
onDescriptionUpdate={onDescriptionUpdate}
|
|
||||||
onThreadLinkSelect={onThreadLinkSelect}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
@ -869,7 +840,32 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
</Col>
|
</Col>
|
||||||
<Col className="p-y-md" span={24}>
|
<Col className="p-y-md" span={24}>
|
||||||
{activeTab === 1 && (
|
{activeTab === 1 && (
|
||||||
<Fragment>{getSchemaTableList()}</Fragment>
|
<Card className="h-full">
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col data-testid="description-container" span={24}>
|
||||||
|
<Description
|
||||||
|
description={description}
|
||||||
|
entityFieldThreads={getEntityFieldThreadCounts(
|
||||||
|
EntityField.DESCRIPTION,
|
||||||
|
entityFieldThreadCount
|
||||||
|
)}
|
||||||
|
entityFqn={databaseSchemaFQN}
|
||||||
|
entityName={databaseSchemaName}
|
||||||
|
entityType={EntityType.DATABASE_SCHEMA}
|
||||||
|
hasEditAccess={
|
||||||
|
databaseSchemaPermission.EditDescription ||
|
||||||
|
databaseSchemaPermission.EditAll
|
||||||
|
}
|
||||||
|
isEdit={isEdit}
|
||||||
|
onCancel={onCancel}
|
||||||
|
onDescriptionEdit={onDescriptionEdit}
|
||||||
|
onDescriptionUpdate={onDescriptionUpdate}
|
||||||
|
onThreadLinkSelect={onThreadLinkSelect}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
{getSchemaTableList()}
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
)}
|
)}
|
||||||
{activeTab === 2 && (
|
{activeTab === 2 && (
|
||||||
<Card className="p-t-xss p-b-md">
|
<Card className="p-t-xss p-b-md">
|
||||||
@ -914,7 +910,7 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</PageLayoutV1>
|
||||||
</PageContainerV1>
|
</PageContainerV1>
|
||||||
) : (
|
) : (
|
||||||
<ErrorPlaceHolder>
|
<ErrorPlaceHolder>
|
||||||
|
@ -181,6 +181,10 @@ jest.mock('react-router-dom', () => ({
|
|||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('components/containers/PageLayoutV1', () => {
|
||||||
|
return jest.fn().mockImplementation(({ children }) => children);
|
||||||
|
});
|
||||||
|
|
||||||
describe('Tests for DatabaseSchemaPage', () => {
|
describe('Tests for DatabaseSchemaPage', () => {
|
||||||
it('Page should render properly for "Tables" tab', async () => {
|
it('Page should render properly for "Tables" tab', async () => {
|
||||||
act(() => {
|
act(() => {
|
||||||
|
@ -76,7 +76,6 @@ import {
|
|||||||
import { getEntityFeedLink, getEntityName } from '../../utils/EntityUtils';
|
import { getEntityFeedLink, getEntityName } from '../../utils/EntityUtils';
|
||||||
import { deletePost, updateThreadData } from '../../utils/FeedUtils';
|
import { deletePost, updateThreadData } from '../../utils/FeedUtils';
|
||||||
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
||||||
import { serviceTypeLogo } from '../../utils/ServiceUtils';
|
|
||||||
import { showErrorToast } from '../../utils/ToastUtils';
|
import { showErrorToast } from '../../utils/ToastUtils';
|
||||||
|
|
||||||
const DatasetDetailsPage: FunctionComponent = () => {
|
const DatasetDetailsPage: FunctionComponent = () => {
|
||||||
@ -234,7 +233,6 @@ const DatasetDetailsPage: FunctionComponent = () => {
|
|||||||
ServiceCategory.DATABASE_SERVICES
|
ServiceCategory.DATABASE_SERVICES
|
||||||
)
|
)
|
||||||
: '',
|
: '',
|
||||||
imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: getPartialNameFromTableFQN(databaseFullyQualifiedName, [
|
name: getPartialNameFromTableFQN(databaseFullyQualifiedName, [
|
||||||
@ -251,11 +249,6 @@ const DatasetDetailsPage: FunctionComponent = () => {
|
|||||||
databaseSchemaFullyQualifiedName
|
databaseSchemaFullyQualifiedName
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: getEntityName(res),
|
|
||||||
url: '',
|
|
||||||
activeTitle: true,
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
addToRecentViewed({
|
addToRecentViewed({
|
||||||
|
@ -206,7 +206,6 @@ const EntityVersionPage: FunctionComponent = () => {
|
|||||||
tags = [],
|
tags = [],
|
||||||
database,
|
database,
|
||||||
service,
|
service,
|
||||||
serviceType,
|
|
||||||
databaseSchema,
|
databaseSchema,
|
||||||
} = res;
|
} = res;
|
||||||
const serviceName = service?.name ?? '';
|
const serviceName = service?.name ?? '';
|
||||||
@ -219,7 +218,6 @@ const EntityVersionPage: FunctionComponent = () => {
|
|||||||
ServiceCategory.DATABASE_SERVICES
|
ServiceCategory.DATABASE_SERVICES
|
||||||
)
|
)
|
||||||
: '',
|
: '',
|
||||||
imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: getPartialNameFromTableFQN(
|
name: getPartialNameFromTableFQN(
|
||||||
@ -237,11 +235,6 @@ const EntityVersionPage: FunctionComponent = () => {
|
|||||||
databaseSchema?.fullyQualifiedName ?? ''
|
databaseSchema?.fullyQualifiedName ?? ''
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: getEntityName(res),
|
|
||||||
url: '',
|
|
||||||
activeTitle: true,
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
getTableVersions(id)
|
getTableVersions(id)
|
||||||
|
@ -51,7 +51,6 @@ import {
|
|||||||
defaultFields,
|
defaultFields,
|
||||||
getFormattedPipelineDetails,
|
getFormattedPipelineDetails,
|
||||||
} from '../../utils/PipelineDetailsUtils';
|
} from '../../utils/PipelineDetailsUtils';
|
||||||
import { serviceTypeLogo } from '../../utils/ServiceUtils';
|
|
||||||
import { showErrorToast } from '../../utils/ToastUtils';
|
import { showErrorToast } from '../../utils/ToastUtils';
|
||||||
|
|
||||||
const PipelineDetailsPage = () => {
|
const PipelineDetailsPage = () => {
|
||||||
@ -134,12 +133,6 @@ const PipelineDetailsPage = () => {
|
|||||||
ServiceCategory.PIPELINE_SERVICES
|
ServiceCategory.PIPELINE_SERVICES
|
||||||
)
|
)
|
||||||
: '',
|
: '',
|
||||||
imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: getEntityName(res),
|
|
||||||
url: '',
|
|
||||||
activeTitle: true,
|
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -58,7 +58,6 @@ import {
|
|||||||
import { getEntityFeedLink, getEntityName } from '../../utils/EntityUtils';
|
import { getEntityFeedLink, getEntityName } from '../../utils/EntityUtils';
|
||||||
import { deletePost, updateThreadData } from '../../utils/FeedUtils';
|
import { deletePost, updateThreadData } from '../../utils/FeedUtils';
|
||||||
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
||||||
import { serviceTypeLogo } from '../../utils/ServiceUtils';
|
|
||||||
import { showErrorToast } from '../../utils/ToastUtils';
|
import { showErrorToast } from '../../utils/ToastUtils';
|
||||||
import {
|
import {
|
||||||
getCurrentTopicTab,
|
getCurrentTopicTab,
|
||||||
@ -210,12 +209,6 @@ const TopicDetailsPage: FunctionComponent = () => {
|
|||||||
ServiceCategory.MESSAGING_SERVICES
|
ServiceCategory.MESSAGING_SERVICES
|
||||||
)
|
)
|
||||||
: '',
|
: '',
|
||||||
imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: getEntityName(res),
|
|
||||||
url: '',
|
|
||||||
activeTitle: true,
|
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -333,12 +333,13 @@ jest.mock('components/common/DeleteWidget/DeleteWidgetModal', () => {
|
|||||||
<p data-testid="delete-entity">DeleteWidgetModal component</p>
|
<p data-testid="delete-entity">DeleteWidgetModal component</p>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const mockObserve = jest.fn();
|
|
||||||
const mockunObserve = jest.fn();
|
|
||||||
|
|
||||||
window.IntersectionObserver = jest.fn().mockImplementation(() => ({
|
jest.mock('components/containers/PageLayoutV1', () => {
|
||||||
observe: mockObserve,
|
return jest.fn().mockImplementation(({ children }) => children);
|
||||||
unobserve: mockunObserve,
|
});
|
||||||
|
|
||||||
|
jest.mock('components/Entity/EntityHeader/EntityHeader.component', () => ({
|
||||||
|
EntityHeader: jest.fn().mockImplementation(() => <p>EntityHeader</p>),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('Test DatabaseDetails page', () => {
|
describe('Test DatabaseDetails page', () => {
|
||||||
@ -347,8 +348,7 @@ describe('Test DatabaseDetails page', () => {
|
|||||||
wrapper: MemoryRouter,
|
wrapper: MemoryRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
const pageContainer = await findByTestId(container, 'page-container');
|
const entityHeader = await findByText(container, 'EntityHeader');
|
||||||
const titleBreadcrumb = await findByText(container, /TitleBreadcrumb/i);
|
|
||||||
const descriptionContainer = await findByTestId(
|
const descriptionContainer = await findByTestId(
|
||||||
container,
|
container,
|
||||||
'description-container'
|
'description-container'
|
||||||
@ -358,8 +358,7 @@ describe('Test DatabaseDetails page', () => {
|
|||||||
'database-databaseSchemas'
|
'database-databaseSchemas'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(pageContainer).toBeInTheDocument();
|
expect(entityHeader).toBeInTheDocument();
|
||||||
expect(titleBreadcrumb).toBeInTheDocument();
|
|
||||||
expect(descriptionContainer).toBeInTheDocument();
|
expect(descriptionContainer).toBeInTheDocument();
|
||||||
expect(databaseTable).toBeInTheDocument();
|
expect(databaseTable).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
@ -420,8 +419,7 @@ describe('Test DatabaseDetails page', () => {
|
|||||||
wrapper: MemoryRouter,
|
wrapper: MemoryRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
const pageContainer = await findByTestId(container, 'page-container');
|
const entityHeader = await findByText(container, 'EntityHeader');
|
||||||
const titleBreadcrumb = await findByText(container, /TitleBreadcrumb/i);
|
|
||||||
const descriptionContainer = await findByTestId(
|
const descriptionContainer = await findByTestId(
|
||||||
container,
|
container,
|
||||||
'description-container'
|
'description-container'
|
||||||
@ -431,8 +429,7 @@ describe('Test DatabaseDetails page', () => {
|
|||||||
'database-databaseSchemas'
|
'database-databaseSchemas'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(pageContainer).toBeInTheDocument();
|
expect(entityHeader).toBeInTheDocument();
|
||||||
expect(titleBreadcrumb).toBeInTheDocument();
|
|
||||||
expect(descriptionContainer).toBeInTheDocument();
|
expect(descriptionContainer).toBeInTheDocument();
|
||||||
expect(databaseTable).toBeInTheDocument();
|
expect(databaseTable).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
@ -23,9 +23,10 @@ import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlac
|
|||||||
import NextPrevious from 'components/common/next-previous/NextPrevious';
|
import NextPrevious from 'components/common/next-previous/NextPrevious';
|
||||||
import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer';
|
import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer';
|
||||||
import TabsPane from 'components/common/TabsPane/TabsPane';
|
import TabsPane from 'components/common/TabsPane/TabsPane';
|
||||||
import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component';
|
|
||||||
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
|
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
|
||||||
import PageContainerV1 from 'components/containers/PageContainerV1';
|
import PageContainerV1 from 'components/containers/PageContainerV1';
|
||||||
|
import PageLayoutV1 from 'components/containers/PageLayoutV1';
|
||||||
|
import { EntityHeader } from 'components/Entity/EntityHeader/EntityHeader.component';
|
||||||
import Loader from 'components/Loader/Loader';
|
import Loader from 'components/Loader/Loader';
|
||||||
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
||||||
import {
|
import {
|
||||||
@ -314,12 +315,6 @@ const DatabaseDetails: FunctionComponent = () => {
|
|||||||
ServiceCategory.DATABASE_SERVICES
|
ServiceCategory.DATABASE_SERVICES
|
||||||
)
|
)
|
||||||
: '',
|
: '',
|
||||||
imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: getEntityName(res),
|
|
||||||
url: '',
|
|
||||||
activeTitle: true,
|
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
fetchDatabaseSchemasAndDBTModels();
|
fetchDatabaseSchemasAndDBTModels();
|
||||||
@ -658,10 +653,10 @@ const DatabaseDetails: FunctionComponent = () => {
|
|||||||
<>
|
<>
|
||||||
{databasePermission.ViewAll || databasePermission.ViewBasic ? (
|
{databasePermission.ViewAll || databasePermission.ViewBasic ? (
|
||||||
<PageContainerV1>
|
<PageContainerV1>
|
||||||
<Row
|
<PageLayoutV1
|
||||||
className=" p-x-md p-t-lg"
|
pageTitle={t('label.entity-detail-plural', {
|
||||||
data-testid="page-container"
|
entity: getEntityName(database),
|
||||||
gutter={[0, 12]}>
|
})}>
|
||||||
{isDatabaseDetailsLoading ? (
|
{isDatabaseDetailsLoading ? (
|
||||||
<Skeleton
|
<Skeleton
|
||||||
active
|
active
|
||||||
@ -672,20 +667,31 @@ const DatabaseDetails: FunctionComponent = () => {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Col span={24}>
|
{database && (
|
||||||
<Space align="center" className="justify-between w-full">
|
<EntityHeader
|
||||||
<TitleBreadcrumb titleLinks={slashedDatabaseName} />
|
breadcrumb={slashedDatabaseName}
|
||||||
<ManageButton
|
entityData={database}
|
||||||
isRecursiveDelete
|
entityType={EntityType.DATABASE}
|
||||||
allowSoftDelete={false}
|
extra={
|
||||||
canDelete={databasePermission.Delete}
|
<ManageButton
|
||||||
entityFQN={databaseFQN}
|
isRecursiveDelete
|
||||||
entityId={databaseId}
|
allowSoftDelete={false}
|
||||||
entityName={databaseName}
|
canDelete={databasePermission.Delete}
|
||||||
entityType={EntityType.DATABASE}
|
entityFQN={databaseFQN}
|
||||||
/>
|
entityId={databaseId}
|
||||||
</Space>
|
entityName={databaseName}
|
||||||
</Col>
|
entityType={EntityType.DATABASE}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
icon={
|
||||||
|
<img
|
||||||
|
className="h-8"
|
||||||
|
src={serviceTypeLogo(serviceType ?? '')}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
{extraInfo.map((info, index) => (
|
{extraInfo.map((info, index) => (
|
||||||
<Space key={index}>
|
<Space key={index}>
|
||||||
@ -809,7 +815,7 @@ const DatabaseDetails: FunctionComponent = () => {
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</PageLayoutV1>
|
||||||
</PageContainerV1>
|
</PageContainerV1>
|
||||||
) : (
|
) : (
|
||||||
<ErrorPlaceHolder>
|
<ErrorPlaceHolder>
|
||||||
|
@ -271,6 +271,10 @@ jest.mock('../../utils/ToastUtils', () => ({
|
|||||||
showErrorToast: jest.fn(),
|
showErrorToast: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('components/containers/PageLayoutV1', () => {
|
||||||
|
return jest.fn().mockImplementation(({ children }) => children);
|
||||||
|
});
|
||||||
|
|
||||||
describe('Test ServicePage Component', () => {
|
describe('Test ServicePage Component', () => {
|
||||||
it('Component should render', async () => {
|
it('Component should render', async () => {
|
||||||
const { container } = render(<ServicePage />, {
|
const { container } = render(<ServicePage />, {
|
||||||
|
@ -24,10 +24,11 @@ import ProfilePicture from 'components/common/ProfilePicture/ProfilePicture';
|
|||||||
import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer';
|
import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer';
|
||||||
import TabsPane from 'components/common/TabsPane/TabsPane';
|
import TabsPane from 'components/common/TabsPane/TabsPane';
|
||||||
import TestConnection from 'components/common/TestConnection/TestConnection';
|
import TestConnection from 'components/common/TestConnection/TestConnection';
|
||||||
import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component';
|
|
||||||
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
|
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
|
||||||
import PageContainerV1 from 'components/containers/PageContainerV1';
|
import PageContainerV1 from 'components/containers/PageContainerV1';
|
||||||
|
import PageLayoutV1 from 'components/containers/PageLayoutV1';
|
||||||
import DataModelTable from 'components/DataModels/DataModelsTable';
|
import DataModelTable from 'components/DataModels/DataModelsTable';
|
||||||
|
import { EntityHeader } from 'components/Entity/EntityHeader/EntityHeader.component';
|
||||||
import Ingestion from 'components/Ingestion/Ingestion.component';
|
import Ingestion from 'components/Ingestion/Ingestion.component';
|
||||||
import Loader from 'components/Loader/Loader';
|
import Loader from 'components/Loader/Loader';
|
||||||
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
||||||
@ -702,7 +703,7 @@ const ServicePage: FunctionComponent = () => {
|
|||||||
getServiceByFQN(serviceName, serviceFQN, 'owner')
|
getServiceByFQN(serviceName, serviceFQN, 'owner')
|
||||||
.then((resService) => {
|
.then((resService) => {
|
||||||
if (resService) {
|
if (resService) {
|
||||||
const { description, serviceType } = resService;
|
const { description } = resService;
|
||||||
setServiceDetails(resService);
|
setServiceDetails(resService);
|
||||||
setConnectionDetails(
|
setConnectionDetails(
|
||||||
resService.connection?.config as DashboardConnection
|
resService.connection?.config as DashboardConnection
|
||||||
@ -716,12 +717,6 @@ const ServicePage: FunctionComponent = () => {
|
|||||||
getServiceRouteFromServiceType(serviceName)
|
getServiceRouteFromServiceType(serviceName)
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: getEntityName(resService),
|
|
||||||
url: '',
|
|
||||||
imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined,
|
|
||||||
activeTitle: true,
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
getOtherDetails();
|
getOtherDetails();
|
||||||
} else {
|
} else {
|
||||||
@ -788,6 +783,30 @@ const ServicePage: FunctionComponent = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRemoveOwner = async () => {
|
||||||
|
const updatedData = {
|
||||||
|
...serviceDetails,
|
||||||
|
owner: undefined,
|
||||||
|
} as ServicesUpdateRequest;
|
||||||
|
|
||||||
|
const jsonPatch = compare(serviceDetails || {}, updatedData);
|
||||||
|
try {
|
||||||
|
const res = await updateOwnerService(
|
||||||
|
serviceName,
|
||||||
|
serviceDetails?.id ?? '',
|
||||||
|
jsonPatch
|
||||||
|
);
|
||||||
|
setServiceDetails(res);
|
||||||
|
} catch (error) {
|
||||||
|
showErrorToast(
|
||||||
|
error as AxiosError,
|
||||||
|
t('server.entity-updating-error', {
|
||||||
|
entity: t('label.owner-lowercase'),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleUpdateOwner = (owner: ServicesType['owner']) => {
|
const handleUpdateOwner = (owner: ServicesType['owner']) => {
|
||||||
if (isUndefined(owner)) {
|
if (isUndefined(owner)) {
|
||||||
handleRemoveOwner();
|
handleRemoveOwner();
|
||||||
@ -832,30 +851,6 @@ const ServicePage: FunctionComponent = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveOwner = async () => {
|
|
||||||
const updatedData = {
|
|
||||||
...serviceDetails,
|
|
||||||
owner: undefined,
|
|
||||||
} as ServicesUpdateRequest;
|
|
||||||
|
|
||||||
const jsonPatch = compare(serviceDetails || {}, updatedData);
|
|
||||||
try {
|
|
||||||
const res = await updateOwnerService(
|
|
||||||
serviceName,
|
|
||||||
serviceDetails?.id ?? '',
|
|
||||||
jsonPatch
|
|
||||||
);
|
|
||||||
setServiceDetails(res);
|
|
||||||
} catch (error) {
|
|
||||||
showErrorToast(
|
|
||||||
error as AxiosError,
|
|
||||||
t('server.entity-updating-error', {
|
|
||||||
entity: t('label.owner-lowercase'),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDescriptionEdit = (): void => {
|
const onDescriptionEdit = (): void => {
|
||||||
setIsEdit(true);
|
setIsEdit(true);
|
||||||
};
|
};
|
||||||
@ -1018,69 +1013,53 @@ const ServicePage: FunctionComponent = () => {
|
|||||||
{getEntityMissingError(serviceName as string, serviceFQN)}
|
{getEntityMissingError(serviceName as string, serviceFQN)}
|
||||||
</ErrorPlaceHolder>
|
</ErrorPlaceHolder>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<PageLayoutV1
|
||||||
|
pageTitle={t('label.entity-detail-plural', {
|
||||||
|
entity: getEntityName(serviceDetails),
|
||||||
|
})}>
|
||||||
{servicePermission.ViewAll || servicePermission.ViewBasic ? (
|
{servicePermission.ViewAll || servicePermission.ViewBasic ? (
|
||||||
<Row
|
<Row data-testid="service-page" gutter={[0, 12]}>
|
||||||
className="p-x-md p-t-lg"
|
{serviceDetails && (
|
||||||
data-testid="service-page"
|
<EntityHeader
|
||||||
gutter={[0, 12]}>
|
breadcrumb={slashedTableName}
|
||||||
<Col span={24}>
|
entityData={serviceDetails}
|
||||||
<Space align="center" className="justify-between w-full">
|
extra={
|
||||||
<TitleBreadcrumb titleLinks={slashedTableName} />
|
serviceDetails?.serviceType !==
|
||||||
{serviceDetails?.serviceType !==
|
MetadataServiceType.OpenMetadata && (
|
||||||
MetadataServiceType.OpenMetadata && (
|
<Tooltip
|
||||||
<Tooltip
|
placement="topRight"
|
||||||
placement="topRight"
|
title={
|
||||||
title={
|
!servicePermission.Delete &&
|
||||||
!servicePermission.Delete &&
|
t('message.no-permission-for-action')
|
||||||
t('message.no-permission-for-action')
|
}>
|
||||||
}>
|
<Button
|
||||||
<Button
|
ghost
|
||||||
ghost
|
data-testid="service-delete"
|
||||||
data-testid="service-delete"
|
disabled={!servicePermission.Delete}
|
||||||
disabled={!servicePermission.Delete}
|
icon={
|
||||||
icon={
|
<IcDeleteColored
|
||||||
<IcDeleteColored
|
className="anticon"
|
||||||
className="anticon"
|
height={14}
|
||||||
height={14}
|
viewBox="0 0 24 24"
|
||||||
viewBox="0 0 24 24"
|
width={14}
|
||||||
width={14}
|
/>
|
||||||
/>
|
}
|
||||||
}
|
size="small"
|
||||||
size="small"
|
type="primary"
|
||||||
type="primary"
|
onClick={handleDelete}>
|
||||||
onClick={handleDelete}>
|
{t('label.delete')}
|
||||||
{t('label.delete')}
|
</Button>
|
||||||
</Button>
|
</Tooltip>
|
||||||
</Tooltip>
|
)
|
||||||
)}
|
}
|
||||||
<DeleteWidgetModal
|
icon={
|
||||||
isRecursiveDelete
|
<img
|
||||||
afterDeleteAction={() =>
|
className="h-8"
|
||||||
history.push(
|
src={serviceTypeLogo(serviceDetails.serviceType)}
|
||||||
getSettingPath(
|
/>
|
||||||
GlobalSettingsMenuCategory.SERVICES,
|
}
|
||||||
SERVICE_CATEGORY_TYPE[
|
/>
|
||||||
serviceCategory as keyof typeof SERVICE_CATEGORY_TYPE
|
)}
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
allowSoftDelete={false}
|
|
||||||
deleteMessage={getDeleteEntityMessage(
|
|
||||||
serviceName || '',
|
|
||||||
paging.total,
|
|
||||||
schemaCount,
|
|
||||||
tableCount
|
|
||||||
)}
|
|
||||||
entityId={serviceDetails?.id}
|
|
||||||
entityName={serviceDetails?.name || ''}
|
|
||||||
entityType={serviceName?.slice(0, -1)}
|
|
||||||
visible={deleteWidgetVisible}
|
|
||||||
onCancel={() => setDeleteWidgetVisible(false)}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
</Col>
|
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Space>
|
<Space>
|
||||||
{extraInfo.map((info) => (
|
{extraInfo.map((info) => (
|
||||||
@ -1222,7 +1201,33 @@ const ServicePage: FunctionComponent = () => {
|
|||||||
{t('message.no-permission-to-view')}
|
{t('message.no-permission-to-view')}
|
||||||
</ErrorPlaceHolder>
|
</ErrorPlaceHolder>
|
||||||
)}
|
)}
|
||||||
</>
|
|
||||||
|
<DeleteWidgetModal
|
||||||
|
isRecursiveDelete
|
||||||
|
afterDeleteAction={() =>
|
||||||
|
history.push(
|
||||||
|
getSettingPath(
|
||||||
|
GlobalSettingsMenuCategory.SERVICES,
|
||||||
|
SERVICE_CATEGORY_TYPE[
|
||||||
|
serviceCategory as keyof typeof SERVICE_CATEGORY_TYPE
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
allowSoftDelete={false}
|
||||||
|
deleteMessage={getDeleteEntityMessage(
|
||||||
|
serviceName || '',
|
||||||
|
paging.total,
|
||||||
|
schemaCount,
|
||||||
|
tableCount
|
||||||
|
)}
|
||||||
|
entityId={serviceDetails?.id}
|
||||||
|
entityName={serviceDetails?.name || ''}
|
||||||
|
entityType={serviceName?.slice(0, -1)}
|
||||||
|
visible={deleteWidgetVisible}
|
||||||
|
onCancel={() => setDeleteWidgetVisible(false)}
|
||||||
|
/>
|
||||||
|
</PageLayoutV1>
|
||||||
)}
|
)}
|
||||||
</PageContainerV1>
|
</PageContainerV1>
|
||||||
);
|
);
|
||||||
|
@ -529,8 +529,9 @@ const TagsPage = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
/**
|
/**
|
||||||
* Fetch all classifications initially
|
* Fetch all classifications initially
|
||||||
|
* Do not set current if we already have currentClassification set
|
||||||
*/
|
*/
|
||||||
fetchClassifications(true);
|
fetchClassifications(!tagCategoryName);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -47,17 +47,6 @@
|
|||||||
box-shadow: @panels-shadow-color;
|
box-shadow: @panels-shadow-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
//font weight
|
|
||||||
.font-300 {
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
.font-medium {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
.text-600 {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
// text color
|
// text color
|
||||||
.text-primary {
|
.text-primary {
|
||||||
color: @primary;
|
color: @primary;
|
||||||
@ -244,17 +233,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Font Weight
|
|
||||||
.font-normal {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
.font-semibold {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
.font-bold {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.transform-180 {
|
.transform-180 {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
@ -293,6 +271,8 @@
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
|
color: @text-color !important;
|
||||||
|
text-decoration: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Opacity CSS start */
|
/* Opacity CSS start */
|
||||||
|
@ -34,18 +34,3 @@ button {
|
|||||||
color: initial;
|
color: initial;
|
||||||
opacity: 0.45;
|
opacity: 0.45;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckBox
|
|
||||||
.ant-checkbox-inner {
|
|
||||||
border-width: 2px;
|
|
||||||
height: 18px;
|
|
||||||
width: 18px;
|
|
||||||
border-color: #7147e8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-checkbox-inner::after {
|
|
||||||
top: 48%;
|
|
||||||
left: 18.5%;
|
|
||||||
width: 6.25px;
|
|
||||||
height: 10px;
|
|
||||||
}
|
|
||||||
|
@ -140,6 +140,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Height
|
//Height
|
||||||
|
.h-inherit {
|
||||||
|
height: inherit;
|
||||||
|
}
|
||||||
.h-3 {
|
.h-3 {
|
||||||
height: 12px;
|
height: 12px;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
//font weight
|
//font weight
|
||||||
|
.font-thin {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
.font-normal {
|
.font-normal {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
@ -164,3 +164,8 @@
|
|||||||
.float-right {
|
.float-right {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vertical Align
|
||||||
|
.vertical-baseline {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
@ -18,12 +18,19 @@ import {
|
|||||||
LeafNodes,
|
LeafNodes,
|
||||||
LineagePos,
|
LineagePos,
|
||||||
} from 'components/EntityLineage/EntityLineage.interface';
|
} from 'components/EntityLineage/EntityLineage.interface';
|
||||||
import { EntityUnion } from 'components/Explore/explore.interface';
|
import {
|
||||||
|
EntityUnion,
|
||||||
|
EntityWithServices,
|
||||||
|
} from 'components/Explore/explore.interface';
|
||||||
import { ResourceEntity } from 'components/PermissionProvider/PermissionProvider.interface';
|
import { ResourceEntity } from 'components/PermissionProvider/PermissionProvider.interface';
|
||||||
|
import { SearchedDataProps } from 'components/searched-data/SearchedData.interface';
|
||||||
import { ExplorePageTabs } from 'enums/Explore.enum';
|
import { ExplorePageTabs } from 'enums/Explore.enum';
|
||||||
|
import { Tag } from 'generated/entity/classification/tag';
|
||||||
import { Container } from 'generated/entity/data/container';
|
import { Container } from 'generated/entity/data/container';
|
||||||
import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel';
|
import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel';
|
||||||
|
import { GlossaryTerm } from 'generated/entity/data/glossaryTerm';
|
||||||
import { Mlmodel } from 'generated/entity/data/mlmodel';
|
import { Mlmodel } from 'generated/entity/data/mlmodel';
|
||||||
|
import { Topic } from 'generated/entity/data/topic';
|
||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
import { get, isEmpty, isNil, isUndefined, lowerCase, startCase } from 'lodash';
|
import { get, isEmpty, isNil, isUndefined, lowerCase, startCase } from 'lodash';
|
||||||
import { Bucket, EntityDetailUnion } from 'Models';
|
import { Bucket, EntityDetailUnion } from 'Models';
|
||||||
@ -34,8 +41,13 @@ import {
|
|||||||
getDashboardDetailsPath,
|
getDashboardDetailsPath,
|
||||||
getDatabaseDetailsPath,
|
getDatabaseDetailsPath,
|
||||||
getDatabaseSchemaDetailsPath,
|
getDatabaseSchemaDetailsPath,
|
||||||
|
getGlossaryTermDetailsPath,
|
||||||
|
getMlModelDetailsPath,
|
||||||
|
getPipelineDetailsPath,
|
||||||
getServiceDetailsPath,
|
getServiceDetailsPath,
|
||||||
getTableDetailsPath,
|
getTableDetailsPath,
|
||||||
|
getTagsDetailsPath,
|
||||||
|
getTopicDetailsPath,
|
||||||
} from '../constants/constants';
|
} from '../constants/constants';
|
||||||
import { AssetsType, EntityType, FqnPart } from '../enums/entity.enum';
|
import { AssetsType, EntityType, FqnPart } from '../enums/entity.enum';
|
||||||
import { SearchIndex } from '../enums/search.enum';
|
import { SearchIndex } from '../enums/search.enum';
|
||||||
@ -58,6 +70,9 @@ import {
|
|||||||
getPartialNameFromTableFQN,
|
getPartialNameFromTableFQN,
|
||||||
getTableFQNFromColumnFQN,
|
getTableFQNFromColumnFQN,
|
||||||
} from './CommonUtils';
|
} from './CommonUtils';
|
||||||
|
import { getContainerDetailPath } from './ContainerDetailUtils';
|
||||||
|
import Fqn from './Fqn';
|
||||||
|
import { getGlossaryPath } from './RouterUtils';
|
||||||
import {
|
import {
|
||||||
getDataTypeString,
|
getDataTypeString,
|
||||||
getTierFromTableTags,
|
getTierFromTableTags,
|
||||||
@ -884,3 +899,151 @@ export const getEntityReferenceListFromEntities = <
|
|||||||
|
|
||||||
return entities.map((entity) => getEntityReferenceFromEntity(entity, type));
|
return entities.map((entity) => getEntityReferenceFromEntity(entity, type));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getBreadcrumbForTable = (
|
||||||
|
entity: Table,
|
||||||
|
includeCurrent = false
|
||||||
|
) => {
|
||||||
|
const { service, database, databaseSchema } = entity;
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: getEntityName(service),
|
||||||
|
url: service?.name
|
||||||
|
? getServiceDetailsPath(
|
||||||
|
service?.name,
|
||||||
|
ServiceCategory.DATABASE_SERVICES
|
||||||
|
)
|
||||||
|
: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: getEntityName(database),
|
||||||
|
url: getDatabaseDetailsPath(database?.fullyQualifiedName ?? ''),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: getEntityName(databaseSchema),
|
||||||
|
url: getDatabaseSchemaDetailsPath(
|
||||||
|
databaseSchema?.fullyQualifiedName ?? ''
|
||||||
|
),
|
||||||
|
},
|
||||||
|
...(includeCurrent
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
name: getEntityName(entity),
|
||||||
|
url: '#',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBreadcrumbForEntitiesWithServiceOnly = (
|
||||||
|
entity: EntityWithServices,
|
||||||
|
includeCurrent = false
|
||||||
|
) => {
|
||||||
|
const { service } = entity;
|
||||||
|
const serviceType =
|
||||||
|
service?.type === 'objectStoreService'
|
||||||
|
? ServiceCategory.STORAGE_SERVICES
|
||||||
|
: service?.type;
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: getEntityName(service),
|
||||||
|
url: service?.name
|
||||||
|
? getServiceDetailsPath(service?.name, serviceType)
|
||||||
|
: '',
|
||||||
|
},
|
||||||
|
...(includeCurrent
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
name: getEntityName(entity),
|
||||||
|
url: '#',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getEntityBreadcrumbs = (
|
||||||
|
entity: SearchedDataProps['data'][number]['_source'],
|
||||||
|
entityType?: EntityType,
|
||||||
|
includeCurrent = false
|
||||||
|
) => {
|
||||||
|
switch (entityType) {
|
||||||
|
case EntityType.TABLE:
|
||||||
|
return getBreadcrumbForTable(entity as Table, includeCurrent);
|
||||||
|
case EntityType.GLOSSARY:
|
||||||
|
case EntityType.GLOSSARY_TERM:
|
||||||
|
// eslint-disable-next-line no-case-declarations
|
||||||
|
const glossary = (entity as GlossaryTerm).glossary;
|
||||||
|
// eslint-disable-next-line no-case-declarations
|
||||||
|
const fqnList = Fqn.split((entity as GlossaryTerm).fullyQualifiedName);
|
||||||
|
// eslint-disable-next-line no-case-declarations
|
||||||
|
const tree = fqnList.slice(1, fqnList.length - 1);
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: glossary.fullyQualifiedName,
|
||||||
|
url: getGlossaryPath(glossary.fullyQualifiedName),
|
||||||
|
},
|
||||||
|
...tree.map((fqn, index, source) => ({
|
||||||
|
name: fqn,
|
||||||
|
url: getGlossaryPath(
|
||||||
|
`${glossary.fullyQualifiedName}.${source
|
||||||
|
.slice(0, index + 1)
|
||||||
|
.join('.')}`
|
||||||
|
),
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
case EntityType.TAG:
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: getEntityName((entity as Tag).classification),
|
||||||
|
url: getTagsDetailsPath(
|
||||||
|
(entity as Tag).classification?.fullyQualifiedName ?? ''
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
case EntityType.TOPIC:
|
||||||
|
case EntityType.DASHBOARD:
|
||||||
|
case EntityType.PIPELINE:
|
||||||
|
case EntityType.MLMODEL:
|
||||||
|
case EntityType.CONTAINER:
|
||||||
|
default:
|
||||||
|
return getBreadcrumbForEntitiesWithServiceOnly(
|
||||||
|
entity as Topic,
|
||||||
|
includeCurrent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getEntityLinkFromType = (
|
||||||
|
fullyQualifiedName: string,
|
||||||
|
entityType: EntityType
|
||||||
|
) => {
|
||||||
|
switch (entityType) {
|
||||||
|
case EntityType.TABLE:
|
||||||
|
return getTableDetailsPath(fullyQualifiedName);
|
||||||
|
case EntityType.GLOSSARY:
|
||||||
|
case EntityType.GLOSSARY_TERM:
|
||||||
|
return getGlossaryTermDetailsPath(fullyQualifiedName);
|
||||||
|
case EntityType.TAG:
|
||||||
|
return getTagsDetailsPath(fullyQualifiedName);
|
||||||
|
case EntityType.TOPIC:
|
||||||
|
return getTopicDetailsPath(fullyQualifiedName);
|
||||||
|
case EntityType.DASHBOARD:
|
||||||
|
return getDashboardDetailsPath(fullyQualifiedName);
|
||||||
|
case EntityType.PIPELINE:
|
||||||
|
return getPipelineDetailsPath(fullyQualifiedName);
|
||||||
|
case EntityType.MLMODEL:
|
||||||
|
return getMlModelDetailsPath(fullyQualifiedName);
|
||||||
|
case EntityType.CONTAINER:
|
||||||
|
return getContainerDetailPath(fullyQualifiedName);
|
||||||
|
case EntityType.DATABASE:
|
||||||
|
return getDatabaseDetailsPath(fullyQualifiedName);
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { ReactComponent as ContainerIcon } from 'assets/svg/ic-storage.svg';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import {
|
import {
|
||||||
OperationPermission,
|
OperationPermission,
|
||||||
@ -301,6 +302,8 @@ export const serviceTypeLogo = (type: string) => {
|
|||||||
logo = DATABASE_DEFAULT;
|
logo = DATABASE_DEFAULT;
|
||||||
} else if (serviceTypes.mlmodelServices.includes(type)) {
|
} else if (serviceTypes.mlmodelServices.includes(type)) {
|
||||||
logo = ML_MODEL_DEFAULT;
|
logo = ML_MODEL_DEFAULT;
|
||||||
|
} else if (serviceTypes.storageServices.includes(type)) {
|
||||||
|
logo = ContainerIcon;
|
||||||
} else {
|
} else {
|
||||||
logo = DEFAULT_SERVICE;
|
logo = DEFAULT_SERVICE;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,9 @@
|
|||||||
import Icon from '@ant-design/icons';
|
import Icon from '@ant-design/icons';
|
||||||
import { Tooltip } from 'antd';
|
import { Tooltip } from 'antd';
|
||||||
import { ExpandableConfig } from 'antd/lib/table/interface';
|
import { ExpandableConfig } from 'antd/lib/table/interface';
|
||||||
|
import { ReactComponent as IconFlatFolder } from 'assets/svg/folder.svg';
|
||||||
import { ReactComponent as ContainerIcon } from 'assets/svg/ic-storage.svg';
|
import { ReactComponent as ContainerIcon } from 'assets/svg/ic-storage.svg';
|
||||||
|
import { ReactComponent as IconTag } from 'assets/svg/tag-grey.svg';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { SourceType } from 'components/searched-data/SearchedData.interface';
|
import { SourceType } from 'components/searched-data/SearchedData.interface';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
@ -272,15 +274,15 @@ export const getEntityLink = (
|
|||||||
|
|
||||||
export const getServiceIcon = (source: SourceType) => {
|
export const getServiceIcon = (source: SourceType) => {
|
||||||
if (source.entityType === EntityType.GLOSSARY_TERM) {
|
if (source.entityType === EntityType.GLOSSARY_TERM) {
|
||||||
return <SVGIcons alt="icon" className="m-r-xs" icon={Icons.FLAT_FOLDER} />;
|
return <IconFlatFolder className="h-9" />;
|
||||||
} else if (source.entityType === EntityType.TAG) {
|
} else if (source.entityType === EntityType.TAG) {
|
||||||
return <SVGIcons alt="icon" className="m-r-xs" icon={Icons.TAG} />;
|
return <IconTag className="h-9" />;
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
alt="service-icon"
|
alt="service-icon"
|
||||||
className="inline h-8 p-r-xs"
|
className="inline h-9"
|
||||||
src={serviceTypeLogo((source.serviceType || source.entityType) ?? '')}
|
src={serviceTypeLogo(source.serviceType || '')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user