mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-09 07:42:41 +00:00
chore(ui): update add button styling for expandable cards (#21197)
* chore(ui): update add button styling for expandable cards * fix tests * fix test for expandable card * fix data test id * fix playwright tests * fix tests * fix metric playwright * fix test
This commit is contained in:
parent
76834ce90a
commit
b74816ceed
@ -211,10 +211,14 @@ entities.forEach((EntityClass) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Tag Add, Update and Remove', async ({ page }) => {
|
test('Tag Add, Update and Remove', async ({ page }) => {
|
||||||
|
test.slow(true);
|
||||||
|
|
||||||
await entity.tag(page, 'PersonalData.Personal', 'PII.None');
|
await entity.tag(page, 'PersonalData.Personal', 'PII.None');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Glossary Term Add, Update and Remove', async ({ page }) => {
|
test('Glossary Term Add, Update and Remove', async ({ page }) => {
|
||||||
|
test.slow(true);
|
||||||
|
|
||||||
await entity.glossaryTerm(
|
await entity.glossaryTerm(
|
||||||
page,
|
page,
|
||||||
EntityDataClass.glossaryTerm1.responseData,
|
EntityDataClass.glossaryTerm1.responseData,
|
||||||
|
@ -224,7 +224,7 @@ export class EntityClass {
|
|||||||
await page
|
await page
|
||||||
.getByTestId('KnowledgePanel.Tags')
|
.getByTestId('KnowledgePanel.Tags')
|
||||||
.getByTestId('tags-container')
|
.getByTestId('tags-container')
|
||||||
.getByTestId('Add')
|
.getByTestId('add-tag')
|
||||||
.isVisible();
|
.isVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +265,7 @@ export class EntityClass {
|
|||||||
await page
|
await page
|
||||||
.locator(`[${rowSelector}="${rowId}"]`)
|
.locator(`[${rowSelector}="${rowId}"]`)
|
||||||
.getByTestId('tags-container')
|
.getByTestId('tags-container')
|
||||||
.getByTestId('Add')
|
.getByTestId('add-tag')
|
||||||
.isVisible();
|
.isVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,7 +281,7 @@ export class EntityClass {
|
|||||||
await page
|
await page
|
||||||
.getByTestId('KnowledgePanel.GlossaryTerms')
|
.getByTestId('KnowledgePanel.GlossaryTerms')
|
||||||
.getByTestId('glossary-container')
|
.getByTestId('glossary-container')
|
||||||
.getByTestId('Add')
|
.getByTestId('add-tag')
|
||||||
.isVisible();
|
.isVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +321,7 @@ export class EntityClass {
|
|||||||
await page
|
await page
|
||||||
.locator(`[${rowSelector}="${rowId}"]`)
|
.locator(`[${rowSelector}="${rowId}"]`)
|
||||||
.getByTestId('glossary-container')
|
.getByTestId('glossary-container')
|
||||||
.getByTestId('Add')
|
.getByTestId('add-tag')
|
||||||
.isVisible();
|
.isVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,14 +532,14 @@ export const checkDataConsumerPermissions = async (page: Page) => {
|
|||||||
// Check right panel add tags button
|
// Check right panel add tags button
|
||||||
await expect(
|
await expect(
|
||||||
page.locator(
|
page.locator(
|
||||||
'[data-testid="KnowledgePanel.Tags"] [data-testid="tags-container"] [data-testid="entity-tags"] .tag-chip-add-button'
|
'[data-testid="KnowledgePanel.Tags"] [data-testid="tags-container"] [data-testid="add-tag"]'
|
||||||
)
|
)
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
// Check right panel add glossary term button
|
// Check right panel add glossary term button
|
||||||
await expect(
|
await expect(
|
||||||
page.locator(
|
page.locator(
|
||||||
'[data-testid="KnowledgePanel.GlossaryTerms"] [data-testid="glossary-container"] [data-testid="entity-tags"] .tag-chip-add-button'
|
'[data-testid="KnowledgePanel.GlossaryTerms"] [data-testid="glossary-container"] [data-testid="add-tag"]'
|
||||||
)
|
)
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
@ -617,14 +617,14 @@ export const checkStewardPermissions = async (page: Page) => {
|
|||||||
// Check right panel add tags button
|
// Check right panel add tags button
|
||||||
await expect(
|
await expect(
|
||||||
page.locator(
|
page.locator(
|
||||||
'[data-testid="KnowledgePanel.Tags"] [data-testid="tags-container"] [data-testid="entity-tags"] .tag-chip-add-button'
|
'[data-testid="KnowledgePanel.Tags"] [data-testid="tags-container"] [data-testid="add-tag"]'
|
||||||
)
|
)
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
// Check right panel add glossary term button
|
// Check right panel add glossary term button
|
||||||
await expect(
|
await expect(
|
||||||
page.locator(
|
page.locator(
|
||||||
'[data-testid="KnowledgePanel.GlossaryTerms"] [data-testid="glossary-container"] [data-testid="entity-tags"] .tag-chip-add-button'
|
'[data-testid="KnowledgePanel.GlossaryTerms"] [data-testid="glossary-container"] [data-testid="add-tag"]'
|
||||||
)
|
)
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
|
@ -14,13 +14,14 @@ import { Typography } from 'antd';
|
|||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { ReactComponent as PlusIcon } from '../../../assets/svg/plus-primary.svg';
|
|
||||||
import { TabSpecificField } from '../../../enums/entity.enum';
|
import { TabSpecificField } from '../../../enums/entity.enum';
|
||||||
import { EntityReference } from '../../../generated/entity/type';
|
import { EntityReference } from '../../../generated/entity/type';
|
||||||
import { getOwnerVersionLabel } from '../../../utils/EntityVersionUtils';
|
import { getOwnerVersionLabel } from '../../../utils/EntityVersionUtils';
|
||||||
import ExpandableCard from '../../common/ExpandableCard/ExpandableCard';
|
import ExpandableCard from '../../common/ExpandableCard/ExpandableCard';
|
||||||
import { EditIconButton } from '../../common/IconButtons/EditIconButton';
|
import {
|
||||||
import TagButton from '../../common/TagButton/TagButton.component';
|
EditIconButton,
|
||||||
|
PlusIconButton,
|
||||||
|
} from '../../common/IconButtons/EditIconButton';
|
||||||
import { UserTeamSelectableList } from '../../common/UserTeamSelectableList/UserTeamSelectableList.component';
|
import { UserTeamSelectableList } from '../../common/UserTeamSelectableList/UserTeamSelectableList.component';
|
||||||
import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider';
|
import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider';
|
||||||
|
|
||||||
@ -48,15 +49,22 @@ export const OwnerLabelV2 = <
|
|||||||
<Typography.Text className="text-sm font-medium">
|
<Typography.Text className="text-sm font-medium">
|
||||||
{t('label.owner-plural')}
|
{t('label.owner-plural')}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
{(permissions.EditOwners || permissions.EditAll) &&
|
{(permissions.EditOwners || permissions.EditAll) && (
|
||||||
data.owners &&
|
|
||||||
data.owners.length > 0 && (
|
|
||||||
<UserTeamSelectableList
|
<UserTeamSelectableList
|
||||||
hasPermission={permissions.EditOwners || permissions.EditAll}
|
hasPermission={permissions.EditOwners || permissions.EditAll}
|
||||||
listHeight={200}
|
listHeight={200}
|
||||||
multiple={{ user: true, team: false }}
|
multiple={{ user: true, team: false }}
|
||||||
owner={data.owners}
|
owner={data.owners}
|
||||||
onUpdate={handleUpdatedOwner}>
|
onUpdate={handleUpdatedOwner}>
|
||||||
|
{isEmpty(data.owners) ? (
|
||||||
|
<PlusIconButton
|
||||||
|
data-testid="add-owner"
|
||||||
|
size="small"
|
||||||
|
title={t('label.add-entity', {
|
||||||
|
entity: t('label.owner-plural'),
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<EditIconButton
|
<EditIconButton
|
||||||
newLook
|
newLook
|
||||||
data-testid="edit-owner"
|
data-testid="edit-owner"
|
||||||
@ -65,6 +73,7 @@ export const OwnerLabelV2 = <
|
|||||||
entity: t('label.owner-plural'),
|
entity: t('label.owner-plural'),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</UserTeamSelectableList>
|
</UserTeamSelectableList>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -85,24 +94,6 @@ export const OwnerLabelV2 = <
|
|||||||
TabSpecificField.OWNERS,
|
TabSpecificField.OWNERS,
|
||||||
permissions.EditOwners || permissions.EditAll
|
permissions.EditOwners || permissions.EditAll
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{data.owners?.length === 0 &&
|
|
||||||
(permissions.EditOwners || permissions.EditAll) && (
|
|
||||||
<UserTeamSelectableList
|
|
||||||
hasPermission={permissions.EditOwners || permissions.EditAll}
|
|
||||||
listHeight={200}
|
|
||||||
multiple={{ user: true, team: false }}
|
|
||||||
owner={data.owners}
|
|
||||||
onUpdate={(updatedUser) => handleUpdatedOwner(updatedUser)}>
|
|
||||||
<TagButton
|
|
||||||
className="text-primary cursor-pointer"
|
|
||||||
dataTestId="add-owner"
|
|
||||||
icon={<PlusIcon height={16} name="plus" width={16} />}
|
|
||||||
label={t('label.add')}
|
|
||||||
tooltip=""
|
|
||||||
/>
|
|
||||||
</UserTeamSelectableList>
|
|
||||||
)}
|
|
||||||
</ExpandableCard>
|
</ExpandableCard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -13,14 +13,15 @@
|
|||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { ReactComponent as PlusIcon } from '../../../assets/svg/plus-primary.svg';
|
|
||||||
import { TabSpecificField } from '../../../enums/entity.enum';
|
import { TabSpecificField } from '../../../enums/entity.enum';
|
||||||
import { EntityReference } from '../../../generated/entity/type';
|
import { EntityReference } from '../../../generated/entity/type';
|
||||||
import { ChangeDescription } from '../../../generated/type/changeEvent';
|
import { ChangeDescription } from '../../../generated/type/changeEvent';
|
||||||
import { getOwnerVersionLabel } from '../../../utils/EntityVersionUtils';
|
import { getOwnerVersionLabel } from '../../../utils/EntityVersionUtils';
|
||||||
import ExpandableCard from '../../common/ExpandableCard/ExpandableCard';
|
import ExpandableCard from '../../common/ExpandableCard/ExpandableCard';
|
||||||
import { EditIconButton } from '../../common/IconButtons/EditIconButton';
|
import {
|
||||||
import TagButton from '../../common/TagButton/TagButton.component';
|
EditIconButton,
|
||||||
|
PlusIconButton,
|
||||||
|
} from '../../common/IconButtons/EditIconButton';
|
||||||
import { UserTeamSelectableList } from '../../common/UserTeamSelectableList/UserTeamSelectableList.component';
|
import { UserTeamSelectableList } from '../../common/UserTeamSelectableList/UserTeamSelectableList.component';
|
||||||
import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider';
|
import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider';
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ export const ReviewerLabelV2 = <
|
|||||||
data-testid="heading-name">
|
data-testid="heading-name">
|
||||||
{t('label.reviewer-plural')}
|
{t('label.reviewer-plural')}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
{hasEditReviewerAccess && hasReviewers && (
|
{hasEditReviewerAccess && (
|
||||||
<UserTeamSelectableList
|
<UserTeamSelectableList
|
||||||
previewSelected
|
previewSelected
|
||||||
hasPermission={hasEditReviewerAccess}
|
hasPermission={hasEditReviewerAccess}
|
||||||
@ -80,6 +81,7 @@ export const ReviewerLabelV2 = <
|
|||||||
owner={assignedReviewers ?? []}
|
owner={assignedReviewers ?? []}
|
||||||
popoverProps={{ placement: 'topLeft' }}
|
popoverProps={{ placement: 'topLeft' }}
|
||||||
onUpdate={handleReviewerSave}>
|
onUpdate={handleReviewerSave}>
|
||||||
|
{hasReviewers ? (
|
||||||
<EditIconButton
|
<EditIconButton
|
||||||
newLook
|
newLook
|
||||||
data-testid="edit-reviewer-button"
|
data-testid="edit-reviewer-button"
|
||||||
@ -88,6 +90,15 @@ export const ReviewerLabelV2 = <
|
|||||||
entity: t('label.reviewer-plural'),
|
entity: t('label.reviewer-plural'),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<PlusIconButton
|
||||||
|
data-testid="Add"
|
||||||
|
size="small"
|
||||||
|
title={t('label.add-entity', {
|
||||||
|
entity: t('label.reviewer-plural'),
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</UserTeamSelectableList>
|
</UserTeamSelectableList>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -102,6 +113,7 @@ export const ReviewerLabelV2 = <
|
|||||||
}}
|
}}
|
||||||
dataTestId="glossary-reviewer"
|
dataTestId="glossary-reviewer"
|
||||||
isExpandDisabled={!hasReviewers}>
|
isExpandDisabled={!hasReviewers}>
|
||||||
|
{hasReviewers ? (
|
||||||
<div data-testid="glossary-reviewer-name">
|
<div data-testid="glossary-reviewer-name">
|
||||||
{getOwnerVersionLabel(
|
{getOwnerVersionLabel(
|
||||||
data,
|
data,
|
||||||
@ -110,25 +122,7 @@ export const ReviewerLabelV2 = <
|
|||||||
hasEditReviewerAccess
|
hasEditReviewerAccess
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
) : null}
|
||||||
{hasEditReviewerAccess && !hasReviewers && (
|
|
||||||
<UserTeamSelectableList
|
|
||||||
previewSelected
|
|
||||||
hasPermission={hasEditReviewerAccess}
|
|
||||||
label={t('label.reviewer-plural')}
|
|
||||||
listHeight={200}
|
|
||||||
multiple={{ user: true, team: false }}
|
|
||||||
owner={assignedReviewers ?? []}
|
|
||||||
popoverProps={{ placement: 'topLeft' }}
|
|
||||||
onUpdate={handleReviewerSave}>
|
|
||||||
<TagButton
|
|
||||||
className="text-primary cursor-pointer"
|
|
||||||
icon={<PlusIcon height={16} name="plus" width={16} />}
|
|
||||||
label={t('label.add')}
|
|
||||||
tooltip=""
|
|
||||||
/>
|
|
||||||
</UserTeamSelectableList>
|
|
||||||
)}
|
|
||||||
</ExpandableCard>
|
</ExpandableCard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,30 +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 { DataProduct } from '../../../generated/entity/domains/dataProduct';
|
|
||||||
import { Paging } from '../../../generated/type/paging';
|
|
||||||
import { DataProductSelectOption } from '../DataProductsSelectList/DataProductSelectList.interface';
|
|
||||||
|
|
||||||
export type DataProductsSelectFormProps = {
|
|
||||||
placeholder: string;
|
|
||||||
defaultValue: string[];
|
|
||||||
onChange?: (value: string[]) => void;
|
|
||||||
onSubmit: (values: DataProduct[]) => Promise<void>;
|
|
||||||
onCancel: () => void;
|
|
||||||
fetchApi: (
|
|
||||||
search: string,
|
|
||||||
page: number
|
|
||||||
) => Promise<{
|
|
||||||
data: DataProductSelectOption[];
|
|
||||||
paging: Paging;
|
|
||||||
}>;
|
|
||||||
};
|
|
@ -1,79 +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 { render } from '@testing-library/react';
|
|
||||||
import userEvent from '@testing-library/user-event';
|
|
||||||
import React from 'react';
|
|
||||||
import DataProductsSelectForm from './DataProductsSelectForm';
|
|
||||||
|
|
||||||
const mockOnSubmit = jest.fn();
|
|
||||||
const mockOnCancel = jest.fn();
|
|
||||||
|
|
||||||
describe('Data Products Form Page', () => {
|
|
||||||
it('renders without errors', () => {
|
|
||||||
const { getByTestId } = render(
|
|
||||||
<DataProductsSelectForm
|
|
||||||
defaultValue={[]}
|
|
||||||
fetchApi={() => Promise.resolve({ data: [], paging: { total: 0 } })}
|
|
||||||
placeholder="Select products"
|
|
||||||
onCancel={mockOnCancel}
|
|
||||||
onSubmit={mockOnSubmit}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
// Ensure that the component renders without errors
|
|
||||||
expect(getByTestId('data-product-selector')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls onCancel function when Cancel button is clicked', () => {
|
|
||||||
const { getByTestId } = render(
|
|
||||||
<DataProductsSelectForm
|
|
||||||
defaultValue={[]}
|
|
||||||
fetchApi={() => Promise.resolve({ data: [], paging: { total: 0 } })}
|
|
||||||
placeholder="Select products"
|
|
||||||
onCancel={mockOnCancel}
|
|
||||||
onSubmit={mockOnSubmit}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
const cancelButton = getByTestId('cancelAssociatedTag');
|
|
||||||
userEvent.click(cancelButton);
|
|
||||||
|
|
||||||
// Ensure that the onCancel function is called when the Cancel button is clicked
|
|
||||||
expect(mockOnCancel).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls onSubmit when the Save button is clicked', () => {
|
|
||||||
const { getByTestId } = render(
|
|
||||||
<DataProductsSelectForm
|
|
||||||
defaultValue={[]}
|
|
||||||
fetchApi={() => Promise.resolve({ data: [], paging: { total: 0 } })}
|
|
||||||
placeholder="Select products"
|
|
||||||
onCancel={mockOnCancel}
|
|
||||||
onSubmit={mockOnSubmit}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
const selectRef = getByTestId('data-product-selector').querySelector(
|
|
||||||
'.ant-select-selector'
|
|
||||||
);
|
|
||||||
if (selectRef) {
|
|
||||||
userEvent.click(selectRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simulate the Save button click
|
|
||||||
userEvent.click(getByTestId('saveAssociatedTag'));
|
|
||||||
|
|
||||||
// Check if onSubmit was called with the selected value
|
|
||||||
expect(mockOnSubmit).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mockOnSubmit).toHaveBeenCalledWith(expect.any(Array));
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,74 +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 { CheckOutlined, CloseOutlined } from '@ant-design/icons';
|
|
||||||
import { Button, Col, Row, Space } from 'antd';
|
|
||||||
import React, { useRef, useState } from 'react';
|
|
||||||
|
|
||||||
import { DataProductsSelectRef } from '../DataProductsSelectList/DataProductSelectList.interface';
|
|
||||||
import DataProductsSelectList from '../DataProductsSelectList/DataProductsSelectList';
|
|
||||||
import { DataProductsSelectFormProps } from './DataProductsSelectForm.interface';
|
|
||||||
|
|
||||||
const DataProductsSelectForm = ({
|
|
||||||
fetchApi,
|
|
||||||
defaultValue,
|
|
||||||
placeholder,
|
|
||||||
onSubmit,
|
|
||||||
onCancel,
|
|
||||||
}: DataProductsSelectFormProps) => {
|
|
||||||
const [isSubmitLoading, setIsSubmitLoading] = useState(false);
|
|
||||||
const selectRef = useRef<DataProductsSelectRef>(null);
|
|
||||||
|
|
||||||
const onSave = () => {
|
|
||||||
setIsSubmitLoading(true);
|
|
||||||
const value = selectRef.current?.getSelectValue() ?? [];
|
|
||||||
onSubmit(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Row gutter={[0, 8]}>
|
|
||||||
<Col className="gutter-row d-flex justify-end" span={24}>
|
|
||||||
<Space align="center">
|
|
||||||
<Button
|
|
||||||
className="p-x-05"
|
|
||||||
data-testid="cancelAssociatedTag"
|
|
||||||
disabled={isSubmitLoading}
|
|
||||||
icon={<CloseOutlined size={12} />}
|
|
||||||
size="small"
|
|
||||||
onClick={onCancel}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
className="p-x-05"
|
|
||||||
data-testid="saveAssociatedTag"
|
|
||||||
icon={<CheckOutlined size={12} />}
|
|
||||||
loading={isSubmitLoading}
|
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
onClick={onSave}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
</Col>
|
|
||||||
|
|
||||||
<Col className="gutter-row" span={24}>
|
|
||||||
<DataProductsSelectList
|
|
||||||
defaultValue={defaultValue}
|
|
||||||
fetchOptions={fetchApi}
|
|
||||||
mode="multiple"
|
|
||||||
placeholder={placeholder}
|
|
||||||
ref={selectRef}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DataProductsSelectForm;
|
|
@ -18,7 +18,6 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { ReactComponent as DataProductIcon } from '../../../assets/svg/ic-data-product.svg';
|
import { ReactComponent as DataProductIcon } from '../../../assets/svg/ic-data-product.svg';
|
||||||
import { NO_DATA_PLACEHOLDER } from '../../../constants/constants';
|
import { NO_DATA_PLACEHOLDER } from '../../../constants/constants';
|
||||||
import { TAG_CONSTANT, TAG_START_WITH } from '../../../constants/Tag.constants';
|
|
||||||
import { EntityType } from '../../../enums/entity.enum';
|
import { EntityType } from '../../../enums/entity.enum';
|
||||||
import { DataProduct } from '../../../generated/entity/domains/dataProduct';
|
import { DataProduct } from '../../../generated/entity/domains/dataProduct';
|
||||||
import { EntityReference } from '../../../generated/entity/type';
|
import { EntityReference } from '../../../generated/entity/type';
|
||||||
@ -26,9 +25,11 @@ import { fetchDataProductsElasticSearch } from '../../../rest/dataProductAPI';
|
|||||||
import { getEntityName } from '../../../utils/EntityUtils';
|
import { getEntityName } from '../../../utils/EntityUtils';
|
||||||
import { getEntityDetailsPath } from '../../../utils/RouterUtils';
|
import { getEntityDetailsPath } from '../../../utils/RouterUtils';
|
||||||
import ExpandableCard from '../../common/ExpandableCard/ExpandableCard';
|
import ExpandableCard from '../../common/ExpandableCard/ExpandableCard';
|
||||||
import { EditIconButton } from '../../common/IconButtons/EditIconButton';
|
import {
|
||||||
import TagsV1 from '../../Tag/TagsV1/TagsV1.component';
|
EditIconButton,
|
||||||
import DataProductsSelectForm from '../DataProductSelectForm/DataProductsSelectForm';
|
PlusIconButton,
|
||||||
|
} from '../../common/IconButtons/EditIconButton';
|
||||||
|
import DataProductsSelectList from '../DataProductsSelectList/DataProductsSelectList';
|
||||||
interface DataProductsContainerProps {
|
interface DataProductsContainerProps {
|
||||||
showHeader?: boolean;
|
showHeader?: boolean;
|
||||||
hasPermission: boolean;
|
hasPermission: boolean;
|
||||||
@ -77,11 +78,13 @@ const DataProductsContainer = ({
|
|||||||
|
|
||||||
const autoCompleteFormSelectContainer = useMemo(() => {
|
const autoCompleteFormSelectContainer = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<DataProductsSelectForm
|
<DataProductsSelectList
|
||||||
|
open
|
||||||
defaultValue={(dataProducts ?? []).map(
|
defaultValue={(dataProducts ?? []).map(
|
||||||
(item) => item.fullyQualifiedName ?? ''
|
(item) => item.fullyQualifiedName ?? ''
|
||||||
)}
|
)}
|
||||||
fetchApi={fetchAPI}
|
fetchOptions={fetchAPI}
|
||||||
|
mode="multiple"
|
||||||
placeholder={t('label.data-product-plural')}
|
placeholder={t('label.data-product-plural')}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
onSubmit={handleSave}
|
onSubmit={handleSave}
|
||||||
@ -139,6 +142,14 @@ const DataProductsContainer = ({
|
|||||||
<Typography.Text className={classNames('text-sm font-medium')}>
|
<Typography.Text className={classNames('text-sm font-medium')}>
|
||||||
{t('label.data-product-plural')}
|
{t('label.data-product-plural')}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
{showAddTagButton && (
|
||||||
|
<PlusIconButton
|
||||||
|
data-testid="add-data-product"
|
||||||
|
size="small"
|
||||||
|
title={t('label.add-data-product')}
|
||||||
|
onClick={handleAddClick}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{hasPermission && !isUndefined(activeDomain) && (
|
{hasPermission && !isUndefined(activeDomain) && (
|
||||||
<Row gutter={12}>
|
<Row gutter={12}>
|
||||||
{!isEmpty(dataProducts) && (
|
{!isEmpty(dataProducts) && (
|
||||||
@ -159,41 +170,26 @@ const DataProductsContainer = ({
|
|||||||
</Space>
|
</Space>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}, [showHeader, dataProducts, hasPermission]);
|
}, [showHeader, dataProducts, hasPermission, showAddTagButton]);
|
||||||
|
|
||||||
const addTagButton = useMemo(
|
|
||||||
() =>
|
|
||||||
showAddTagButton ? (
|
|
||||||
<Col
|
|
||||||
className="m-t-xss"
|
|
||||||
data-testid="add-data-product"
|
|
||||||
onClick={handleAddClick}>
|
|
||||||
<TagsV1 startWith={TAG_START_WITH.PLUS} tag={TAG_CONSTANT} />
|
|
||||||
</Col>
|
|
||||||
) : null,
|
|
||||||
[showAddTagButton]
|
|
||||||
);
|
|
||||||
|
|
||||||
const cardProps = useMemo(() => {
|
const cardProps = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
title: header,
|
title: header,
|
||||||
};
|
};
|
||||||
}, [header]);
|
}, [header, showAddTagButton, isEditMode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExpandableCard
|
<ExpandableCard
|
||||||
cardProps={cardProps}
|
cardProps={cardProps}
|
||||||
dataTestId="data-products-container"
|
dataTestId="data-products-container"
|
||||||
isExpandDisabled={isEmpty(dataProducts)}>
|
isExpandDisabled={isEmpty(dataProducts)}>
|
||||||
{!isEditMode && (
|
{isEditMode ? (
|
||||||
|
autoCompleteFormSelectContainer
|
||||||
|
) : isEmpty(renderDataProducts) ? null : (
|
||||||
<Row data-testid="data-products-list">
|
<Row data-testid="data-products-list">
|
||||||
<Col className="flex flex-wrap gap-2">
|
<Col className="flex flex-wrap gap-2">{renderDataProducts}</Col>
|
||||||
{addTagButton}
|
|
||||||
{renderDataProducts}
|
|
||||||
</Col>
|
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
{isEditMode && autoCompleteFormSelectContainer}
|
|
||||||
</ExpandableCard>
|
</ExpandableCard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
* 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 { SelectProps } from 'antd';
|
||||||
import { DataProduct } from '../../../generated/entity/domains/dataProduct';
|
import { DataProduct } from '../../../generated/entity/domains/dataProduct';
|
||||||
import { Paging } from '../../../generated/type/paging';
|
import { Paging } from '../../../generated/type/paging';
|
||||||
|
|
||||||
@ -18,12 +19,13 @@ export type DataProductSelectOption = {
|
|||||||
value: DataProduct;
|
value: DataProduct;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface DataProductsSelectListProps {
|
export interface DataProductsSelectListProps extends SelectProps {
|
||||||
mode?: 'multiple';
|
mode?: 'multiple';
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
debounceTimeout?: number;
|
debounceTimeout?: number;
|
||||||
defaultValue?: string[];
|
defaultValue?: string[];
|
||||||
onChange?: (newValue: DataProduct[]) => void;
|
onSubmit?: (newValue: DataProduct[]) => Promise<void>;
|
||||||
|
onCancel?: () => void;
|
||||||
fetchOptions: (
|
fetchOptions: (
|
||||||
search: string,
|
search: string,
|
||||||
page: number
|
page: number
|
||||||
|
@ -10,17 +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 { Select, Space, Tooltip, Typography } from 'antd';
|
import { Button, Select, Space, Tooltip, Typography } from 'antd';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import React, {
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
forwardRef,
|
import { useTranslation } from 'react-i18next';
|
||||||
useCallback,
|
|
||||||
useImperativeHandle,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { DataProduct } from '../../../generated/entity/domains/dataProduct';
|
import { DataProduct } from '../../../generated/entity/domains/dataProduct';
|
||||||
import { Paging } from '../../../generated/type/paging';
|
import { Paging } from '../../../generated/type/paging';
|
||||||
import { getEntityName } from '../../../utils/EntityUtils';
|
import { getEntityName } from '../../../utils/EntityUtils';
|
||||||
@ -30,25 +24,17 @@ import Loader from '../../common/Loader/Loader';
|
|||||||
import {
|
import {
|
||||||
DataProductSelectOption,
|
DataProductSelectOption,
|
||||||
DataProductsSelectListProps,
|
DataProductsSelectListProps,
|
||||||
DataProductsSelectRef,
|
|
||||||
} from './DataProductSelectList.interface';
|
} from './DataProductSelectList.interface';
|
||||||
|
|
||||||
const DataProductsSelectList = forwardRef<
|
const DataProductsSelectList = ({
|
||||||
DataProductsSelectRef,
|
|
||||||
DataProductsSelectListProps
|
|
||||||
>(
|
|
||||||
(
|
|
||||||
{
|
|
||||||
mode,
|
mode,
|
||||||
onChange,
|
onSubmit,
|
||||||
|
onCancel,
|
||||||
fetchOptions,
|
fetchOptions,
|
||||||
debounceTimeout = 800,
|
debounceTimeout = 800,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
...props
|
...props
|
||||||
}: DataProductsSelectListProps,
|
}: DataProductsSelectListProps) => {
|
||||||
ref
|
|
||||||
) => {
|
|
||||||
const selectRef = useRef(null);
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [hasContentLoading, setHasContentLoading] = useState(false);
|
const [hasContentLoading, setHasContentLoading] = useState(false);
|
||||||
const [options, setOptions] = useState<DataProductSelectOption[]>([]);
|
const [options, setOptions] = useState<DataProductSelectOption[]>([]);
|
||||||
@ -56,6 +42,19 @@ const DataProductsSelectList = forwardRef<
|
|||||||
const [paging, setPaging] = useState<Paging>({} as Paging);
|
const [paging, setPaging] = useState<Paging>({} as Paging);
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [selectedValue, setSelectedValue] = useState<DataProduct[]>([]);
|
const [selectedValue, setSelectedValue] = useState<DataProduct[]>([]);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [isSubmitLoading, setIsSubmitLoading] = useState(false);
|
||||||
|
|
||||||
|
const onSave = async () => {
|
||||||
|
setIsSubmitLoading(true);
|
||||||
|
try {
|
||||||
|
await onSubmit?.(selectedValue);
|
||||||
|
} catch (error) {
|
||||||
|
showErrorToast(error as AxiosError);
|
||||||
|
} finally {
|
||||||
|
setIsSubmitLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const loadOptions = useCallback(
|
const loadOptions = useCallback(
|
||||||
async (value: string) => {
|
async (value: string) => {
|
||||||
@ -87,9 +86,7 @@ const DataProductsSelectList = forwardRef<
|
|||||||
label: item.label,
|
label: item.label,
|
||||||
displayName: (
|
displayName: (
|
||||||
<Space className="w-full" direction="vertical" size={0}>
|
<Space className="w-full" direction="vertical" size={0}>
|
||||||
<Typography.Paragraph
|
<Typography.Paragraph ellipsis className="text-grey-muted m-0 p-0">
|
||||||
ellipsis
|
|
||||||
className="text-grey-muted m-0 p-0">
|
|
||||||
{getEntityName(item.value.domain)}
|
{getEntityName(item.value.domain)}
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
<Typography.Text ellipsis>
|
<Typography.Text ellipsis>
|
||||||
@ -128,6 +125,22 @@ const DataProductsSelectList = forwardRef<
|
|||||||
<>
|
<>
|
||||||
{menu}
|
{menu}
|
||||||
{hasContentLoading ? <Loader size="small" /> : null}
|
{hasContentLoading ? <Loader size="small" /> : null}
|
||||||
|
<Space className="p-sm p-b-xss p-l-xs custom-dropdown-render" size={8}>
|
||||||
|
<Button
|
||||||
|
className="update-btn"
|
||||||
|
data-testid="saveAssociatedTag"
|
||||||
|
loading={isSubmitLoading}
|
||||||
|
size="small"
|
||||||
|
onClick={onSave}>
|
||||||
|
{t('label.update')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
data-testid="cancelAssociatedTag"
|
||||||
|
size="small"
|
||||||
|
onClick={onCancel}>
|
||||||
|
{t('label.cancel')}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -144,12 +157,6 @@ const DataProductsSelectList = forwardRef<
|
|||||||
setSelectedValue(entityObj as DataProduct[]);
|
setSelectedValue(entityObj as DataProduct[]);
|
||||||
};
|
};
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
|
||||||
getSelectValue() {
|
|
||||||
return selectedValue;
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
autoFocus
|
autoFocus
|
||||||
@ -162,7 +169,6 @@ const DataProductsSelectList = forwardRef<
|
|||||||
mode={mode}
|
mode={mode}
|
||||||
notFoundContent={isLoading ? <Loader size="small" /> : null}
|
notFoundContent={isLoading ? <Loader size="small" /> : null}
|
||||||
optionLabelProp="label"
|
optionLabelProp="label"
|
||||||
ref={selectRef}
|
|
||||||
tagRender={tagRender}
|
tagRender={tagRender}
|
||||||
onChange={onSelectChange}
|
onChange={onSelectChange}
|
||||||
onFocus={() => loadOptions('')}
|
onFocus={() => loadOptions('')}
|
||||||
@ -183,7 +189,6 @@ const DataProductsSelectList = forwardRef<
|
|||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
|
||||||
export default DataProductsSelectList;
|
export default DataProductsSelectList;
|
||||||
|
@ -12,24 +12,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Icon from '@ant-design/icons';
|
import Icon from '@ant-design/icons';
|
||||||
import { Button, Col, Drawer, Row, Space, Tooltip, Typography } from 'antd';
|
import { Col, Drawer, Row, Space, Typography } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { ReactComponent as EditIcon } from '../../../../assets/svg/edit-new.svg';
|
|
||||||
import { ReactComponent as IconUser } from '../../../../assets/svg/user.svg';
|
import { ReactComponent as IconUser } from '../../../../assets/svg/user.svg';
|
||||||
import { DE_ACTIVE_COLOR } from '../../../../constants/constants';
|
|
||||||
import { EntityType } from '../../../../enums/entity.enum';
|
import { EntityType } from '../../../../enums/entity.enum';
|
||||||
import { Query } from '../../../../generated/entity/data/query';
|
import { Query } from '../../../../generated/entity/data/query';
|
||||||
import { TagLabel } from '../../../../generated/type/tagLabel';
|
import { TagLabel, TagSource } from '../../../../generated/type/tagLabel';
|
||||||
import { getEntityName } from '../../../../utils/EntityUtils';
|
import { getEntityName } from '../../../../utils/EntityUtils';
|
||||||
import { getUserPath } from '../../../../utils/RouterUtils';
|
import { getUserPath } from '../../../../utils/RouterUtils';
|
||||||
import DescriptionV1 from '../../../common/EntityDescription/DescriptionV1';
|
import DescriptionV1 from '../../../common/EntityDescription/DescriptionV1';
|
||||||
|
import ExpandableCard from '../../../common/ExpandableCard/ExpandableCard';
|
||||||
|
import { EditIconButton } from '../../../common/IconButtons/EditIconButton';
|
||||||
import Loader from '../../../common/Loader/Loader';
|
import Loader from '../../../common/Loader/Loader';
|
||||||
import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component';
|
import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component';
|
||||||
import ProfilePicture from '../../../common/ProfilePicture/ProfilePicture';
|
import ProfilePicture from '../../../common/ProfilePicture/ProfilePicture';
|
||||||
import { UserTeamSelectableList } from '../../../common/UserTeamSelectableList/UserTeamSelectableList.component';
|
import { UserTeamSelectableList } from '../../../common/UserTeamSelectableList/UserTeamSelectableList.component';
|
||||||
import TagsInput from '../../../TagsInput/TagsInput.component';
|
import TagsContainerV2 from '../../../Tag/TagsContainerV2/TagsContainerV2';
|
||||||
import { TableQueryRightPanelProps } from './TableQueryRightPanel.interface';
|
import { TableQueryRightPanelProps } from './TableQueryRightPanel.interface';
|
||||||
|
|
||||||
const TableQueryRightPanel = ({
|
const TableQueryRightPanel = ({
|
||||||
@ -79,9 +79,11 @@ const TableQueryRightPanel = ({
|
|||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Loader />
|
<Loader />
|
||||||
) : (
|
) : (
|
||||||
<Row className="m-y-md p-x-md w-full" gutter={[16, 40]}>
|
<Row className="m-y-md p-x-md w-full" gutter={[16, 20]}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Space className="relative" direction="vertical" size={4}>
|
<ExpandableCard
|
||||||
|
cardProps={{
|
||||||
|
title: (
|
||||||
<Space align="center" className="w-full" size={0}>
|
<Space align="center" className="w-full" size={0}>
|
||||||
<Typography.Text className="right-panel-label">
|
<Typography.Text className="right-panel-label">
|
||||||
{t('label.owner-plural')}
|
{t('label.owner-plural')}
|
||||||
@ -95,27 +97,25 @@ const TableQueryRightPanel = ({
|
|||||||
onUpdate={(updatedUsers) =>
|
onUpdate={(updatedUsers) =>
|
||||||
handleUpdateOwner(updatedUsers)
|
handleUpdateOwner(updatedUsers)
|
||||||
}>
|
}>
|
||||||
<Tooltip
|
<EditIconButton
|
||||||
|
data-testid="edit-owner"
|
||||||
|
size="small"
|
||||||
title={t('label.edit-entity', {
|
title={t('label.edit-entity', {
|
||||||
entity: t('label.owner-lowercase-plural'),
|
entity: t('label.owner-lowercase-plural'),
|
||||||
})}>
|
})}
|
||||||
<Button
|
|
||||||
className="cursor-pointer flex-center"
|
|
||||||
data-testid="edit-owner"
|
|
||||||
icon={<EditIcon color={DE_ACTIVE_COLOR} width="14px" />}
|
|
||||||
size="small"
|
|
||||||
type="text"
|
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
|
||||||
</UserTeamSelectableList>
|
</UserTeamSelectableList>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
|
),
|
||||||
|
}}>
|
||||||
<OwnerLabel hasPermission={false} owners={query.owners} />
|
<OwnerLabel hasPermission={false} owners={query.owners} />
|
||||||
</Space>
|
</ExpandableCard>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Space direction="vertical" size={4}>
|
|
||||||
<DescriptionV1
|
<DescriptionV1
|
||||||
|
wrapInCard
|
||||||
|
className="w-full"
|
||||||
description={query?.description || ''}
|
description={query?.description || ''}
|
||||||
entityFullyQualifiedName={query?.fullyQualifiedName}
|
entityFullyQualifiedName={query?.fullyQualifiedName}
|
||||||
entityType={EntityType.QUERY}
|
entityType={EntityType.QUERY}
|
||||||
@ -123,22 +123,28 @@ const TableQueryRightPanel = ({
|
|||||||
showCommentsIcon={false}
|
showCommentsIcon={false}
|
||||||
onDescriptionUpdate={onDescriptionUpdate}
|
onDescriptionUpdate={onDescriptionUpdate}
|
||||||
/>
|
/>
|
||||||
</Space>
|
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<TagsInput
|
<TagsContainerV2
|
||||||
editable={EditAll || EditTags}
|
newLook
|
||||||
tags={query?.tags || []}
|
permission={EditAll || EditTags}
|
||||||
onTagsUpdate={handleTagSelection}
|
selectedTags={query?.tags || []}
|
||||||
|
showTaskHandler={false}
|
||||||
|
tagType={TagSource.Classification}
|
||||||
|
onSelectionChange={handleTagSelection}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Space className="m-b-md" direction="vertical" size={4}>
|
<ExpandableCard
|
||||||
|
cardProps={{
|
||||||
|
title: (
|
||||||
<Typography.Text
|
<Typography.Text
|
||||||
className="right-panel-label"
|
className="right-panel-label"
|
||||||
data-testid="users">
|
data-testid="users">
|
||||||
{t('label.user-plural')}
|
{t('label.user-plural')}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
),
|
||||||
|
}}>
|
||||||
{query.users && query.users.length ? (
|
{query.users && query.users.length ? (
|
||||||
<Space wrap size={6}>
|
<Space wrap size={6}>
|
||||||
{query.users.map((user) => (
|
{query.users.map((user) => (
|
||||||
@ -161,15 +167,19 @@ const TableQueryRightPanel = ({
|
|||||||
})}
|
})}
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</ExpandableCard>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Space className="m-b-md" direction="vertical" size={4}>
|
<ExpandableCard
|
||||||
|
cardProps={{
|
||||||
|
title: (
|
||||||
<Typography.Text
|
<Typography.Text
|
||||||
className="right-panel-label"
|
className="right-panel-label"
|
||||||
data-testid="used-by">
|
data-testid="used-by">
|
||||||
{t('label.used-by')}
|
{t('label.used-by')}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
),
|
||||||
|
}}>
|
||||||
{query.usedBy && query.usedBy.length ? (
|
{query.usedBy && query.usedBy.length ? (
|
||||||
<Space wrap size={6}>
|
<Space wrap size={6}>
|
||||||
{query.usedBy.map((user) => (
|
{query.usedBy.map((user) => (
|
||||||
@ -186,7 +196,7 @@ const TableQueryRightPanel = ({
|
|||||||
})}
|
})}
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</ExpandableCard>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
|
@ -83,13 +83,13 @@ jest.mock('../../../common/EntityDescription/DescriptionV1', () => {
|
|||||||
</div>
|
</div>
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
jest.mock('../../../TagsInput/TagsInput.component', () => {
|
jest.mock('../../../Tag/TagsContainerV2/TagsContainerV2', () => {
|
||||||
return jest.fn().mockImplementation(({ onTagsUpdate }) => (
|
return jest.fn().mockImplementation(({ onSelectionChange }) => (
|
||||||
<div>
|
<div>
|
||||||
TagsInput.component
|
TagsInput.component
|
||||||
<button
|
<button
|
||||||
data-testid="update-tags-button"
|
data-testid="update-tags-button"
|
||||||
onClick={() => onTagsUpdate(mockNewTag)}>
|
onClick={() => onSelectionChange(mockNewTag)}>
|
||||||
{' '}
|
{' '}
|
||||||
Update Tags
|
Update Tags
|
||||||
</button>
|
</button>
|
||||||
|
@ -51,7 +51,6 @@ const TableTags = <T extends TableUnion>({
|
|||||||
entityType={entityType}
|
entityType={entityType}
|
||||||
permission={hasTagEditAccess && !isReadOnly}
|
permission={hasTagEditAccess && !isReadOnly}
|
||||||
selectedTags={tags}
|
selectedTags={tags}
|
||||||
showHeader={false}
|
|
||||||
showInlineEditButton={showInlineEditTagButton}
|
showInlineEditButton={showInlineEditTagButton}
|
||||||
sizeCap={TAG_LIST_SIZE}
|
sizeCap={TAG_LIST_SIZE}
|
||||||
tagType={type}
|
tagType={type}
|
||||||
|
@ -15,14 +15,15 @@ import classNames from 'classnames';
|
|||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { cloneDeep, includes, isEmpty, isEqual } from 'lodash';
|
import { cloneDeep, includes, isEmpty, isEqual } from 'lodash';
|
||||||
import { default as React, useMemo } from 'react';
|
import { default as React, useMemo } from 'react';
|
||||||
import { ReactComponent as PlusIcon } from '../../../assets/svg/plus-primary.svg';
|
|
||||||
import { TabSpecificField } from '../../../enums/entity.enum';
|
import { TabSpecificField } from '../../../enums/entity.enum';
|
||||||
import { Domain } from '../../../generated/entity/domains/domain';
|
import { Domain } from '../../../generated/entity/domains/domain';
|
||||||
import { EntityReference } from '../../../generated/tests/testCase';
|
import { EntityReference } from '../../../generated/tests/testCase';
|
||||||
import { getOwnerVersionLabel } from '../../../utils/EntityVersionUtils';
|
import { getOwnerVersionLabel } from '../../../utils/EntityVersionUtils';
|
||||||
import ExpandableCard from '../../common/ExpandableCard/ExpandableCard';
|
import ExpandableCard from '../../common/ExpandableCard/ExpandableCard';
|
||||||
import { EditIconButton } from '../../common/IconButtons/EditIconButton';
|
import {
|
||||||
import TagButton from '../../common/TagButton/TagButton.component';
|
EditIconButton,
|
||||||
|
PlusIconButton,
|
||||||
|
} from '../../common/IconButtons/EditIconButton';
|
||||||
import { UserSelectableList } from '../../common/UserSelectableList/UserSelectableList.component';
|
import { UserSelectableList } from '../../common/UserSelectableList/UserSelectableList.component';
|
||||||
import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider';
|
import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider';
|
||||||
|
|
||||||
@ -69,12 +70,21 @@ export const DomainExpertWidget = () => {
|
|||||||
data-testid="domain-expert-heading-name">
|
data-testid="domain-expert-heading-name">
|
||||||
{t('label.expert-plural')}
|
{t('label.expert-plural')}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
{editOwnerPermission && domain.experts && domain.experts.length > 0 && (
|
{editOwnerPermission && (
|
||||||
<UserSelectableList
|
<UserSelectableList
|
||||||
hasPermission
|
hasPermission
|
||||||
popoverProps={{ placement: 'topLeft' }}
|
popoverProps={{ placement: 'topLeft' }}
|
||||||
selectedUsers={domain.experts ?? []}
|
selectedUsers={domain.experts ?? []}
|
||||||
onUpdate={handleExpertsUpdate}>
|
onUpdate={handleExpertsUpdate}>
|
||||||
|
{isEmpty(domain.experts) ? (
|
||||||
|
<PlusIconButton
|
||||||
|
data-testid="Add"
|
||||||
|
size="small"
|
||||||
|
title={t('label.add-entity', {
|
||||||
|
entity: t('label.expert-plural'),
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<EditIconButton
|
<EditIconButton
|
||||||
newLook
|
newLook
|
||||||
data-testid="edit-expert-button"
|
data-testid="edit-expert-button"
|
||||||
@ -83,13 +93,13 @@ export const DomainExpertWidget = () => {
|
|||||||
entity: t('label.expert-plural'),
|
entity: t('label.expert-plural'),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</UserSelectableList>
|
</UserSelectableList>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const content = (
|
const content = isEmpty(domain.experts) ? null : (
|
||||||
<>
|
|
||||||
<div>
|
<div>
|
||||||
{getOwnerVersionLabel(
|
{getOwnerVersionLabel(
|
||||||
domain,
|
domain,
|
||||||
@ -98,24 +108,6 @@ export const DomainExpertWidget = () => {
|
|||||||
editAllPermission
|
editAllPermission
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
{editOwnerPermission && domain.experts?.length === 0 && (
|
|
||||||
<UserSelectableList
|
|
||||||
hasPermission={editOwnerPermission}
|
|
||||||
popoverProps={{ placement: 'topLeft' }}
|
|
||||||
selectedUsers={domain.experts ?? []}
|
|
||||||
onUpdate={handleExpertsUpdate}>
|
|
||||||
<TagButton
|
|
||||||
className="text-primary cursor-pointer"
|
|
||||||
icon={<PlusIcon height={16} name="plus" width={16} />}
|
|
||||||
label={t('label.add')}
|
|
||||||
tooltip=""
|
|
||||||
/>
|
|
||||||
</UserSelectableList>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -15,7 +15,6 @@ import { Space, Typography } from 'antd';
|
|||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { cloneDeep, isEmpty, isEqual } from 'lodash';
|
import { cloneDeep, isEmpty, isEqual } from 'lodash';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { ReactComponent as PlusIcon } from '../../../../assets/svg/plus-primary.svg';
|
|
||||||
import { NO_DATA_PLACEHOLDER } from '../../../../constants/constants';
|
import { NO_DATA_PLACEHOLDER } from '../../../../constants/constants';
|
||||||
import { EntityField } from '../../../../constants/Feeds.constants';
|
import { EntityField } from '../../../../constants/Feeds.constants';
|
||||||
import {
|
import {
|
||||||
@ -30,8 +29,10 @@ import {
|
|||||||
} from '../../../../utils/EntityVersionUtils';
|
} from '../../../../utils/EntityVersionUtils';
|
||||||
import { renderReferenceElement } from '../../../../utils/GlossaryUtils';
|
import { renderReferenceElement } from '../../../../utils/GlossaryUtils';
|
||||||
import ExpandableCard from '../../../common/ExpandableCard/ExpandableCard';
|
import ExpandableCard from '../../../common/ExpandableCard/ExpandableCard';
|
||||||
import { EditIconButton } from '../../../common/IconButtons/EditIconButton';
|
import {
|
||||||
import TagButton from '../../../common/TagButton/TagButton.component';
|
EditIconButton,
|
||||||
|
PlusIconButton,
|
||||||
|
} from '../../../common/IconButtons/EditIconButton';
|
||||||
import { useGenericContext } from '../../../Customization/GenericProvider/GenericProvider';
|
import { useGenericContext } from '../../../Customization/GenericProvider/GenericProvider';
|
||||||
import GlossaryTermReferencesModal from '../GlossaryTermReferencesModal.component';
|
import GlossaryTermReferencesModal from '../GlossaryTermReferencesModal.component';
|
||||||
|
|
||||||
@ -130,7 +131,19 @@ const GlossaryTermReferences = () => {
|
|||||||
<Typography.Text className="text-sm font-medium">
|
<Typography.Text className="text-sm font-medium">
|
||||||
{t('label.reference-plural')}
|
{t('label.reference-plural')}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
{references.length > 0 && permissions.EditAll && (
|
{permissions.EditAll &&
|
||||||
|
(isEmpty(references) ? (
|
||||||
|
<PlusIconButton
|
||||||
|
data-testid="term-references-add-button"
|
||||||
|
size="small"
|
||||||
|
title={t('label.add-entity', {
|
||||||
|
entity: t('label.reference-plural'),
|
||||||
|
})}
|
||||||
|
onClick={() => {
|
||||||
|
setIsViewMode(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<EditIconButton
|
<EditIconButton
|
||||||
newLook
|
newLook
|
||||||
data-testid="edit-button"
|
data-testid="edit-button"
|
||||||
@ -138,11 +151,12 @@ const GlossaryTermReferences = () => {
|
|||||||
size="small"
|
size="small"
|
||||||
onClick={() => setIsViewMode(false)}
|
onClick={() => setIsViewMode(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
))}
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<ExpandableCard
|
<ExpandableCard
|
||||||
cardProps={{
|
cardProps={{
|
||||||
title: header,
|
title: header,
|
||||||
@ -151,26 +165,15 @@ const GlossaryTermReferences = () => {
|
|||||||
isExpandDisabled={isEmpty(references)}>
|
isExpandDisabled={isEmpty(references)}>
|
||||||
{isVersionView ? (
|
{isVersionView ? (
|
||||||
getVersionReferenceElements()
|
getVersionReferenceElements()
|
||||||
) : (
|
) : !permissions.EditAll || !isEmpty(references) ? (
|
||||||
<div className="d-flex flex-wrap">
|
<div className="d-flex flex-wrap">
|
||||||
{references.map((ref) => renderReferenceElement(ref))}
|
{references.map((ref) => renderReferenceElement(ref))}
|
||||||
{permissions.EditAll && references.length === 0 && (
|
|
||||||
<TagButton
|
|
||||||
className="text-primary cursor-pointer"
|
|
||||||
dataTestId="term-references-add-button"
|
|
||||||
icon={<PlusIcon height={16} name="plus" width={16} />}
|
|
||||||
label={t('label.add')}
|
|
||||||
tooltip=""
|
|
||||||
onClick={() => {
|
|
||||||
setIsViewMode(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!permissions.EditAll && references.length === 0 && (
|
{!permissions.EditAll && references.length === 0 && (
|
||||||
<div>{NO_DATA_PLACEHOLDER}</div>
|
<div>{NO_DATA_PLACEHOLDER}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
) : null}
|
||||||
|
</ExpandableCard>
|
||||||
|
|
||||||
<GlossaryTermReferencesModal
|
<GlossaryTermReferencesModal
|
||||||
isVisible={!isViewMode}
|
isVisible={!isViewMode}
|
||||||
@ -180,7 +183,7 @@ const GlossaryTermReferences = () => {
|
|||||||
}}
|
}}
|
||||||
onSave={handleReferencesSave}
|
onSave={handleReferencesSave}
|
||||||
/>
|
/>
|
||||||
</ExpandableCard>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import { Button, Select, Space, Typography } from 'antd';
|
|||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { cloneDeep, isEmpty, isEqual } from 'lodash';
|
import { cloneDeep, isEmpty, isEqual } from 'lodash';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { ReactComponent as PlusIcon } from '../../../../assets/svg/plus-primary.svg';
|
|
||||||
import { NO_DATA_PLACEHOLDER } from '../../../../constants/constants';
|
import { NO_DATA_PLACEHOLDER } from '../../../../constants/constants';
|
||||||
import { EntityField } from '../../../../constants/Feeds.constants';
|
import { EntityField } from '../../../../constants/Feeds.constants';
|
||||||
import { GlossaryTerm } from '../../../../generated/entity/data/glossaryTerm';
|
import { GlossaryTerm } from '../../../../generated/entity/data/glossaryTerm';
|
||||||
@ -27,7 +26,10 @@ import {
|
|||||||
getDiffByFieldName,
|
getDiffByFieldName,
|
||||||
} from '../../../../utils/EntityVersionUtils';
|
} from '../../../../utils/EntityVersionUtils';
|
||||||
import ExpandableCard from '../../../common/ExpandableCard/ExpandableCard';
|
import ExpandableCard from '../../../common/ExpandableCard/ExpandableCard';
|
||||||
import { EditIconButton } from '../../../common/IconButtons/EditIconButton';
|
import {
|
||||||
|
EditIconButton,
|
||||||
|
PlusIconButton,
|
||||||
|
} from '../../../common/IconButtons/EditIconButton';
|
||||||
import TagButton from '../../../common/TagButton/TagButton.component';
|
import TagButton from '../../../common/TagButton/TagButton.component';
|
||||||
import { useGenericContext } from '../../../Customization/GenericProvider/GenericProvider';
|
import { useGenericContext } from '../../../Customization/GenericProvider/GenericProvider';
|
||||||
|
|
||||||
@ -42,7 +44,8 @@ const GlossaryTermSynonyms = () => {
|
|||||||
permissions,
|
permissions,
|
||||||
} = useGenericContext<GlossaryTerm>();
|
} = useGenericContext<GlossaryTerm>();
|
||||||
|
|
||||||
const getSynonyms = () => (
|
const getSynonyms = () =>
|
||||||
|
!permissions.EditAll || !isEmpty(synonyms) ? (
|
||||||
<div className="d-flex flex-wrap">
|
<div className="d-flex flex-wrap">
|
||||||
{synonyms.map((synonym) => (
|
{synonyms.map((synonym) => (
|
||||||
<TagButton
|
<TagButton
|
||||||
@ -51,23 +54,12 @@ const GlossaryTermSynonyms = () => {
|
|||||||
label={synonym}
|
label={synonym}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{permissions.EditAll && synonyms.length === 0 && (
|
|
||||||
<TagButton
|
|
||||||
className="text-primary cursor-pointer"
|
|
||||||
dataTestId="synonym-add-button"
|
|
||||||
icon={<PlusIcon height={16} name="plus" width={16} />}
|
|
||||||
label={t('label.add')}
|
|
||||||
tooltip=""
|
|
||||||
onClick={() => {
|
|
||||||
setIsViewMode(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!permissions.EditAll && synonyms.length === 0 && (
|
{!permissions.EditAll && synonyms.length === 0 && (
|
||||||
<div>{NO_DATA_PLACEHOLDER}</div>
|
<div>{NO_DATA_PLACEHOLDER}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
) : null;
|
||||||
|
|
||||||
const getSynonymsContainer = useCallback(() => {
|
const getSynonymsContainer = useCallback(() => {
|
||||||
if (!isVersionView) {
|
if (!isVersionView) {
|
||||||
@ -174,7 +166,20 @@ const GlossaryTermSynonyms = () => {
|
|||||||
<Typography.Text className="text-sm font-medium">
|
<Typography.Text className="text-sm font-medium">
|
||||||
{t('label.synonym-plural')}
|
{t('label.synonym-plural')}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
{permissions.EditAll && synonyms.length > 0 && isViewMode && (
|
{permissions.EditAll &&
|
||||||
|
isViewMode &&
|
||||||
|
(isEmpty(synonyms) ? (
|
||||||
|
<PlusIconButton
|
||||||
|
data-testid="synonym-add-button"
|
||||||
|
size="small"
|
||||||
|
title={t('label.add-entity', {
|
||||||
|
entity: t('label.synonym-plural'),
|
||||||
|
})}
|
||||||
|
onClick={() => {
|
||||||
|
setIsViewMode(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<EditIconButton
|
<EditIconButton
|
||||||
newLook
|
newLook
|
||||||
data-testid="edit-button"
|
data-testid="edit-button"
|
||||||
@ -184,7 +189,7 @@ const GlossaryTermSynonyms = () => {
|
|||||||
})}
|
})}
|
||||||
onClick={() => setIsViewMode(false)}
|
onClick={() => setIsViewMode(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ import { isArray, isEmpty, isUndefined } from 'lodash';
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { ReactComponent as IconTerm } from '../../../../assets/svg/book.svg';
|
import { ReactComponent as IconTerm } from '../../../../assets/svg/book.svg';
|
||||||
import { ReactComponent as PlusIcon } from '../../../../assets/svg/plus-primary.svg';
|
|
||||||
import TagSelectForm from '../../../../components/Tag/TagsSelectForm/TagsSelectForm.component';
|
import TagSelectForm from '../../../../components/Tag/TagsSelectForm/TagsSelectForm.component';
|
||||||
import { NO_DATA_PLACEHOLDER } from '../../../../constants/constants';
|
import { NO_DATA_PLACEHOLDER } from '../../../../constants/constants';
|
||||||
import { EntityField } from '../../../../constants/Feeds.constants';
|
import { EntityField } from '../../../../constants/Feeds.constants';
|
||||||
@ -41,7 +40,10 @@ import { VersionStatus } from '../../../../utils/EntityVersionUtils.interface';
|
|||||||
import { getGlossaryPath } from '../../../../utils/RouterUtils';
|
import { getGlossaryPath } from '../../../../utils/RouterUtils';
|
||||||
import { SelectOption } from '../../../common/AsyncSelectList/AsyncSelectList.interface';
|
import { SelectOption } from '../../../common/AsyncSelectList/AsyncSelectList.interface';
|
||||||
import ExpandableCard from '../../../common/ExpandableCard/ExpandableCard';
|
import ExpandableCard from '../../../common/ExpandableCard/ExpandableCard';
|
||||||
import { EditIconButton } from '../../../common/IconButtons/EditIconButton';
|
import {
|
||||||
|
EditIconButton,
|
||||||
|
PlusIconButton,
|
||||||
|
} from '../../../common/IconButtons/EditIconButton';
|
||||||
import TagButton from '../../../common/TagButton/TagButton.component';
|
import TagButton from '../../../common/TagButton/TagButton.component';
|
||||||
import { useGenericContext } from '../../../Customization/GenericProvider/GenericProvider';
|
import { useGenericContext } from '../../../Customization/GenericProvider/GenericProvider';
|
||||||
|
|
||||||
@ -193,21 +195,8 @@ const RelatedTerms = () => {
|
|||||||
() =>
|
() =>
|
||||||
isVersionView ? (
|
isVersionView ? (
|
||||||
getVersionRelatedTerms()
|
getVersionRelatedTerms()
|
||||||
) : (
|
) : !permissions.EditAll || !isEmpty(selectedOption) ? (
|
||||||
<div className="d-flex flex-wrap">
|
<div className="d-flex flex-wrap">
|
||||||
{permissions.EditAll && selectedOption.length === 0 && (
|
|
||||||
<TagButton
|
|
||||||
className="text-primary cursor-pointer"
|
|
||||||
dataTestId="related-term-add-button"
|
|
||||||
icon={<PlusIcon height={16} name="plus" width={16} />}
|
|
||||||
label={t('label.add')}
|
|
||||||
tooltip=""
|
|
||||||
onClick={() => {
|
|
||||||
setIsIconVisible(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{selectedOption.map((entity: EntityReference) =>
|
{selectedOption.map((entity: EntityReference) =>
|
||||||
getRelatedTermElement(entity)
|
getRelatedTermElement(entity)
|
||||||
)}
|
)}
|
||||||
@ -216,7 +205,7 @@ const RelatedTerms = () => {
|
|||||||
<div>{NO_DATA_PLACEHOLDER}</div>
|
<div>{NO_DATA_PLACEHOLDER}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
),
|
) : null,
|
||||||
[
|
[
|
||||||
permissions,
|
permissions,
|
||||||
selectedOption,
|
selectedOption,
|
||||||
@ -231,7 +220,19 @@ const RelatedTerms = () => {
|
|||||||
<Typography.Text className="text-sm font-medium">
|
<Typography.Text className="text-sm font-medium">
|
||||||
{t('label.related-term-plural')}
|
{t('label.related-term-plural')}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
{permissions.EditAll && selectedOption.length > 0 && (
|
{permissions.EditAll &&
|
||||||
|
(isEmpty(selectedOption) ? (
|
||||||
|
<PlusIconButton
|
||||||
|
data-testid="related-term-add-button"
|
||||||
|
size="small"
|
||||||
|
title={t('label.add-entity', {
|
||||||
|
entity: t('label.related-term-plural'),
|
||||||
|
})}
|
||||||
|
onClick={() => {
|
||||||
|
setIsIconVisible(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<EditIconButton
|
<EditIconButton
|
||||||
newLook
|
newLook
|
||||||
data-testid="edit-button"
|
data-testid="edit-button"
|
||||||
@ -241,7 +242,7 @@ const RelatedTerms = () => {
|
|||||||
})}
|
})}
|
||||||
onClick={() => setIsIconVisible(false)}
|
onClick={() => setIsIconVisible(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* 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 { Button, Col, Row, Space, Typography } from 'antd';
|
import { Button, Space, Typography } from 'antd';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
@ -18,7 +18,6 @@ import React, { FC, useCallback, useMemo, useState } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { NO_DATA_PLACEHOLDER } from '../../../constants/constants';
|
import { NO_DATA_PLACEHOLDER } from '../../../constants/constants';
|
||||||
import { TAG_CONSTANT, TAG_START_WITH } from '../../../constants/Tag.constants';
|
|
||||||
import { Metric } from '../../../generated/entity/data/metric';
|
import { Metric } from '../../../generated/entity/data/metric';
|
||||||
import { EntityReference } from '../../../generated/type/entityReference';
|
import { EntityReference } from '../../../generated/type/entityReference';
|
||||||
import entityUtilClassBase from '../../../utils/EntityUtilClassBase';
|
import entityUtilClassBase from '../../../utils/EntityUtilClassBase';
|
||||||
@ -26,10 +25,12 @@ import { getEntityName } from '../../../utils/EntityUtils';
|
|||||||
import { getEntityIcon } from '../../../utils/TableUtils';
|
import { getEntityIcon } from '../../../utils/TableUtils';
|
||||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||||
import ExpandableCard from '../../common/ExpandableCard/ExpandableCard';
|
import ExpandableCard from '../../common/ExpandableCard/ExpandableCard';
|
||||||
import { EditIconButton } from '../../common/IconButtons/EditIconButton';
|
import {
|
||||||
|
EditIconButton,
|
||||||
|
PlusIconButton,
|
||||||
|
} from '../../common/IconButtons/EditIconButton';
|
||||||
import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider';
|
import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider';
|
||||||
import { DataAssetOption } from '../../DataAssets/DataAssetAsyncSelectList/DataAssetAsyncSelectList.interface';
|
import { DataAssetOption } from '../../DataAssets/DataAssetAsyncSelectList/DataAssetAsyncSelectList.interface';
|
||||||
import TagsV1 from '../../Tag/TagsV1/TagsV1.component';
|
|
||||||
import './related-metrics.less';
|
import './related-metrics.less';
|
||||||
import { RelatedMetricsForm } from './RelatedMetricsForm';
|
import { RelatedMetricsForm } from './RelatedMetricsForm';
|
||||||
|
|
||||||
@ -160,34 +161,32 @@ const RelatedMetrics: FC<RelatedMetricsProps> = ({
|
|||||||
{t('label.related-metric-plural')}
|
{t('label.related-metric-plural')}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
{!isEdit &&
|
{!isEdit &&
|
||||||
!isEmpty(relatedMetrics) &&
|
|
||||||
permissions.EditAll &&
|
permissions.EditAll &&
|
||||||
!metricDetails.deleted && (
|
!metricDetails.deleted &&
|
||||||
|
(isEmpty(relatedMetrics) ? (
|
||||||
|
<PlusIconButton
|
||||||
|
data-testid="add-related-metrics-container"
|
||||||
|
size="small"
|
||||||
|
title={t('label.add-entity', {
|
||||||
|
entity: t('label.related-metric-plural'),
|
||||||
|
})}
|
||||||
|
onClick={() => setIsEdit(true)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<EditIconButton
|
<EditIconButton
|
||||||
newLook
|
newLook
|
||||||
data-testid="edit-related-metrics"
|
data-testid="edit-related-metrics"
|
||||||
size="small"
|
size="small"
|
||||||
|
title={t('label.edit-entity', {
|
||||||
|
entity: t('label.related-metric-plural'),
|
||||||
|
})}
|
||||||
onClick={() => setIsEdit(true)}
|
onClick={() => setIsEdit(true)}
|
||||||
/>
|
/>
|
||||||
)}
|
))}
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
|
|
||||||
const content = (
|
const content = isEdit ? (
|
||||||
<>
|
|
||||||
{isEmpty(relatedMetrics) &&
|
|
||||||
!isEdit &&
|
|
||||||
permissions.EditAll &&
|
|
||||||
!metricDetails.deleted && (
|
|
||||||
<Col
|
|
||||||
className="m-t-xss"
|
|
||||||
data-testid="add-related-metrics-container"
|
|
||||||
onClick={() => setIsEdit(true)}>
|
|
||||||
<TagsV1 startWith={TAG_START_WITH.PLUS} tag={TAG_CONSTANT} />
|
|
||||||
</Col>
|
|
||||||
)}
|
|
||||||
<Col span={24}>
|
|
||||||
{isEdit ? (
|
|
||||||
<RelatedMetricsForm
|
<RelatedMetricsForm
|
||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
initialOptions={initialOptions}
|
initialOptions={initialOptions}
|
||||||
@ -195,12 +194,10 @@ const RelatedMetrics: FC<RelatedMetricsProps> = ({
|
|||||||
onCancel={() => setIsEdit(false)}
|
onCancel={() => setIsEdit(false)}
|
||||||
onSubmit={handleRelatedMetricUpdate}
|
onSubmit={handleRelatedMetricUpdate}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : isEmpty(relatedMetrics) && (metricDetails.deleted || isInSummaryPanel) ? (
|
||||||
<>
|
|
||||||
{isEmpty(relatedMetrics) &&
|
|
||||||
(metricDetails.deleted || isInSummaryPanel) ? (
|
|
||||||
<Typography.Text>{NO_DATA_PLACEHOLDER}</Typography.Text>
|
<Typography.Text>{NO_DATA_PLACEHOLDER}</Typography.Text>
|
||||||
) : (
|
) : (
|
||||||
|
!isEmpty(relatedMetrics) && (
|
||||||
<div
|
<div
|
||||||
className="metric-entity-list-body"
|
className="metric-entity-list-body"
|
||||||
data-testid="metric-entity-list-body">
|
data-testid="metric-entity-list-body">
|
||||||
@ -208,11 +205,7 @@ const RelatedMetrics: FC<RelatedMetricsProps> = ({
|
|||||||
{isShowMore && getRelatedMetricListing(hiddenRelatedMetrics)}
|
{isShowMore && getRelatedMetricListing(hiddenRelatedMetrics)}
|
||||||
{!isEmpty(hiddenRelatedMetrics) && showMoreLessElement}
|
{!isEmpty(hiddenRelatedMetrics) && showMoreLessElement}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Col>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -221,7 +214,7 @@ const RelatedMetrics: FC<RelatedMetricsProps> = ({
|
|||||||
title: header,
|
title: header,
|
||||||
}}
|
}}
|
||||||
isExpandDisabled={isEmpty(relatedMetrics)}>
|
isExpandDisabled={isEmpty(relatedMetrics)}>
|
||||||
<Row gutter={[0, 8]}>{content}</Row>
|
{content}
|
||||||
</ExpandableCard>
|
</ExpandableCard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -26,7 +26,6 @@ export type TagsContainerV2Props = {
|
|||||||
columnData?: {
|
columnData?: {
|
||||||
fqn: string;
|
fqn: string;
|
||||||
};
|
};
|
||||||
showHeader?: boolean;
|
|
||||||
showBottomEditButton?: boolean;
|
showBottomEditButton?: boolean;
|
||||||
showInlineEditButton?: boolean;
|
showInlineEditButton?: boolean;
|
||||||
children?: ReactElement;
|
children?: ReactElement;
|
||||||
|
@ -43,6 +43,7 @@ import ExpandableCard from '../../common/ExpandableCard/ExpandableCard';
|
|||||||
import {
|
import {
|
||||||
CommentIconButton,
|
CommentIconButton,
|
||||||
EditIconButton,
|
EditIconButton,
|
||||||
|
PlusIconButton,
|
||||||
RequestIconButton,
|
RequestIconButton,
|
||||||
} from '../../common/IconButtons/EditIconButton';
|
} from '../../common/IconButtons/EditIconButton';
|
||||||
import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider';
|
import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider';
|
||||||
@ -65,7 +66,6 @@ const TagsContainerV2 = ({
|
|||||||
tagType,
|
tagType,
|
||||||
displayType,
|
displayType,
|
||||||
layoutType,
|
layoutType,
|
||||||
showHeader = true,
|
|
||||||
showBottomEditButton,
|
showBottomEditButton,
|
||||||
showInlineEditButton,
|
showInlineEditButton,
|
||||||
columnData,
|
columnData,
|
||||||
@ -173,19 +173,24 @@ const TagsContainerV2 = ({
|
|||||||
const addTagButton = useMemo(
|
const addTagButton = useMemo(
|
||||||
() =>
|
() =>
|
||||||
showAddTagButton ? (
|
showAddTagButton ? (
|
||||||
<Col className="m-t-xss" onClick={handleAddClick}>
|
<PlusIconButton
|
||||||
<TagsV1
|
className="m-t-xss"
|
||||||
startWith={TAG_START_WITH.PLUS}
|
data-testid="add-tag"
|
||||||
tag={isGlossaryType ? GLOSSARY_CONSTANT : TAG_CONSTANT}
|
size="small"
|
||||||
tagType={tagType}
|
title={t('label.add-entity', {
|
||||||
|
entity: isGlossaryType
|
||||||
|
? t('label.glossary-term')
|
||||||
|
: t('label.tag-plural'),
|
||||||
|
})}
|
||||||
|
onClick={handleAddClick}
|
||||||
/>
|
/>
|
||||||
</Col>
|
|
||||||
) : null,
|
) : null,
|
||||||
[showAddTagButton]
|
[showAddTagButton, handleAddClick, t, isGlossaryType]
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderTags = useMemo(
|
const renderTags = useMemo(
|
||||||
() => (
|
() =>
|
||||||
|
isEmpty(tags?.[tagType]) && !showNoDataPlaceholder ? null : (
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<TagsViewer
|
<TagsViewer
|
||||||
displayType={displayType}
|
displayType={displayType}
|
||||||
@ -267,7 +272,6 @@ const TagsContainerV2 = ({
|
|||||||
|
|
||||||
const header = useMemo(() => {
|
const header = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
showHeader && (
|
|
||||||
<Space>
|
<Space>
|
||||||
<Typography.Text
|
<Typography.Text
|
||||||
className={classNames({
|
className={classNames({
|
||||||
@ -278,7 +282,7 @@ const TagsContainerV2 = ({
|
|||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
{permission && (
|
{permission && (
|
||||||
<>
|
<>
|
||||||
{!isEmpty(tags?.[tagType]) && !isEditTags && (
|
{addTagButton ?? (
|
||||||
<EditIconButton
|
<EditIconButton
|
||||||
data-testid="edit-button"
|
data-testid="edit-button"
|
||||||
newLook={newLook}
|
newLook={newLook}
|
||||||
@ -301,12 +305,10 @@ const TagsContainerV2 = ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}, [
|
}, [
|
||||||
tags,
|
tags,
|
||||||
tagType,
|
tagType,
|
||||||
showHeader,
|
|
||||||
isEditTags,
|
isEditTags,
|
||||||
permission,
|
permission,
|
||||||
showTaskHandler,
|
showTaskHandler,
|
||||||
@ -372,13 +374,21 @@ const TagsContainerV2 = ({
|
|||||||
} else {
|
} else {
|
||||||
return isHoriZontalLayout ? (
|
return isHoriZontalLayout ? (
|
||||||
horizontalLayout
|
horizontalLayout
|
||||||
) : (
|
) : showInlineEditButton || !isEmpty(renderTags) || !newLook ? (
|
||||||
<Row data-testid="entity-tags">
|
<Row data-testid="entity-tags">
|
||||||
{addTagButton}
|
{showAddTagButton && (
|
||||||
|
<Col className="m-t-xss" onClick={handleAddClick}>
|
||||||
|
<TagsV1
|
||||||
|
startWith={TAG_START_WITH.PLUS}
|
||||||
|
tag={isGlossaryType ? GLOSSARY_CONSTANT : TAG_CONSTANT}
|
||||||
|
tagType={tagType}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
{renderTags}
|
{renderTags}
|
||||||
{showInlineEditButton && <Col>{editTagButton}</Col>}
|
{showInlineEditButton ? <Col>{editTagButton}</Col> : null}
|
||||||
</Row>
|
</Row>
|
||||||
);
|
) : null;
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
isEditTags,
|
isEditTags,
|
||||||
@ -429,17 +439,7 @@ const TagsContainerV2 = ({
|
|||||||
}}
|
}}
|
||||||
dataTestId={isGlossaryType ? 'glossary-container' : 'tags-container'}
|
dataTestId={isGlossaryType ? 'glossary-container' : 'tags-container'}
|
||||||
isExpandDisabled={isEmpty(tags?.[tagType])}>
|
isExpandDisabled={isEmpty(tags?.[tagType])}>
|
||||||
{suggestionDataRender ?? (
|
{suggestionDataRender ?? tagBody}
|
||||||
<>
|
|
||||||
{tagBody}
|
|
||||||
{(children || showBottomEditButton) && (
|
|
||||||
<Space align="baseline" className="m-t-xs w-full" size="middle">
|
|
||||||
{showBottomEditButton && !showInlineEditButton && editTagButton}
|
|
||||||
{children}
|
|
||||||
</Space>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</ExpandableCard>
|
</ExpandableCard>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -448,8 +448,6 @@ const TagsContainerV2 = ({
|
|||||||
<div
|
<div
|
||||||
className="w-full tags-container"
|
className="w-full tags-container"
|
||||||
data-testid={isGlossaryType ? 'glossary-container' : 'tags-container'}>
|
data-testid={isGlossaryType ? 'glossary-container' : 'tags-container'}>
|
||||||
{header}
|
|
||||||
|
|
||||||
{suggestionDataRender ?? (
|
{suggestionDataRender ?? (
|
||||||
<>
|
<>
|
||||||
{tagBody}
|
{tagBody}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2024 Collate.
|
* Copyright 2025 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,136 +10,129 @@
|
|||||||
* 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 { act, render, screen } from '@testing-library/react';
|
import { render, screen } 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 { LabelType, State, TagSource } from '../../generated/type/tagLabel';
|
import {
|
||||||
|
LabelType,
|
||||||
|
State,
|
||||||
|
TagLabel,
|
||||||
|
TagSource,
|
||||||
|
} from '../../generated/type/tagLabel';
|
||||||
|
import TagsContainerV2 from '../Tag/TagsContainerV2/TagsContainerV2';
|
||||||
import TagsInput from './TagsInput.component';
|
import TagsInput from './TagsInput.component';
|
||||||
|
|
||||||
const mockOnTagsUpdate = jest.fn();
|
jest.mock('../../components/Tag/TagsContainerV2/TagsContainerV2', () => {
|
||||||
|
return jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(() => (
|
||||||
|
<div data-testid="tags-container">Mocked TagsContainerV2</div>
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
const tags = [
|
describe('TagsInput Component', () => {
|
||||||
|
const mockTags: TagLabel[] = [
|
||||||
{
|
{
|
||||||
tagFQN: 'tag1',
|
tagFQN: 'test.tag1',
|
||||||
displayName: 'Tag 1',
|
|
||||||
labelType: LabelType.Automated,
|
|
||||||
source: TagSource.Classification,
|
source: TagSource.Classification,
|
||||||
|
labelType: LabelType.Manual,
|
||||||
state: State.Confirmed,
|
state: State.Confirmed,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tagFQN: 'tag2',
|
tagFQN: 'test.tag2',
|
||||||
displayName: 'Tag 2',
|
source: TagSource.Classification,
|
||||||
description: 'This is a sample tag description.',
|
labelType: LabelType.Manual,
|
||||||
labelType: LabelType.Derived,
|
state: State.Confirmed,
|
||||||
source: TagSource.Glossary,
|
|
||||||
state: State.Suggested,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
describe('TagsInput', () => {
|
const mockOnTagsUpdate = jest.fn();
|
||||||
it('should render TagsInput along with tagsViewer in version view', async () => {
|
|
||||||
await act(async () => {
|
it('renders without crashing', () => {
|
||||||
|
render(
|
||||||
|
<TagsInput editable tags={mockTags} onTagsUpdate={mockOnTagsUpdate} />,
|
||||||
|
{ wrapper: MemoryRouter }
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByTestId('tags-input-container')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders in version view mode', () => {
|
||||||
render(
|
render(
|
||||||
<TagsInput
|
<TagsInput
|
||||||
|
editable
|
||||||
isVersionView
|
isVersionView
|
||||||
editable={false}
|
tags={mockTags}
|
||||||
tags={tags}
|
|
||||||
onTagsUpdate={mockOnTagsUpdate}
|
onTagsUpdate={mockOnTagsUpdate}
|
||||||
/>,
|
/>,
|
||||||
{
|
{ wrapper: MemoryRouter }
|
||||||
wrapper: MemoryRouter,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('label.tag-plural')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
it('renders tags in version view mode', () => {
|
||||||
await screen.findByTestId('tags-input-container')
|
|
||||||
).toBeInTheDocument();
|
|
||||||
expect(await screen.findByText('label.tag-plural')).toBeInTheDocument();
|
|
||||||
expect(await screen.findByText('Tag 1')).toBeInTheDocument();
|
|
||||||
expect(await screen.findByText('Tag 2')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render tags container when not in in version view', async () => {
|
|
||||||
await act(async () => {
|
|
||||||
render(
|
render(
|
||||||
<TagsInput
|
<TagsInput
|
||||||
editable={false}
|
editable
|
||||||
tags={tags}
|
isVersionView
|
||||||
|
tags={mockTags}
|
||||||
onTagsUpdate={mockOnTagsUpdate}
|
onTagsUpdate={mockOnTagsUpdate}
|
||||||
/>,
|
/>,
|
||||||
{
|
{ wrapper: MemoryRouter }
|
||||||
wrapper: MemoryRouter,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
mockTags.forEach((tag) => {
|
||||||
|
expect(screen.getByText(tag.tagFQN)).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
it('renders TagsContainerV2 when not in version view', () => {
|
||||||
await screen.findByTestId('tags-input-container')
|
|
||||||
).toBeInTheDocument();
|
|
||||||
expect(await screen.findByTestId('tags-container')).toBeInTheDocument();
|
|
||||||
|
|
||||||
expect(await screen.findByText('label.tag-plural')).toBeInTheDocument();
|
|
||||||
expect(await screen.findByText('Tag 1')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render edit button when no editable', async () => {
|
|
||||||
await act(async () => {
|
|
||||||
render(
|
|
||||||
<TagsInput editable tags={tags} onTagsUpdate={mockOnTagsUpdate} />,
|
|
||||||
{
|
|
||||||
wrapper: MemoryRouter,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(await screen.findByTestId('edit-button')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not render edit button when no editable', async () => {
|
|
||||||
await act(async () => {
|
|
||||||
render(
|
render(
|
||||||
<TagsInput
|
<TagsInput
|
||||||
editable={false}
|
editable
|
||||||
tags={tags}
|
isVersionView={false}
|
||||||
|
tags={mockTags}
|
||||||
onTagsUpdate={mockOnTagsUpdate}
|
onTagsUpdate={mockOnTagsUpdate}
|
||||||
/>,
|
/>,
|
||||||
{
|
{ wrapper: MemoryRouter }
|
||||||
wrapper: MemoryRouter,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(await screen.queryByTestId('edit-button')).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not render tags if tags is empty', async () => {
|
|
||||||
await act(async () => {
|
|
||||||
render(
|
|
||||||
<TagsInput
|
|
||||||
editable={false}
|
|
||||||
tags={[]}
|
|
||||||
onTagsUpdate={mockOnTagsUpdate}
|
|
||||||
/>,
|
|
||||||
{
|
|
||||||
wrapper: MemoryRouter,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(await screen.findByTestId('tags-container')).toBeInTheDocument();
|
// Verify TagsContainerV2 is rendered
|
||||||
expect(await screen.findByTestId('entity-tags')).toBeInTheDocument();
|
expect(screen.getByTestId('tags-container')).toBeInTheDocument();
|
||||||
expect(await screen.findByText('--')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render add tags if tags is empty and has permission', async () => {
|
it('handles empty tags array', () => {
|
||||||
await act(async () => {
|
|
||||||
render(<TagsInput editable tags={[]} onTagsUpdate={mockOnTagsUpdate} />, {
|
render(<TagsInput editable tags={[]} onTagsUpdate={mockOnTagsUpdate} />, {
|
||||||
wrapper: MemoryRouter,
|
wrapper: MemoryRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(await screen.findByTestId('entity-tags')).toBeInTheDocument();
|
expect(screen.getByTestId('tags-input-container')).toBeInTheDocument();
|
||||||
expect(await screen.findByTestId('add-tag')).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('disables tag editing when editable is false', () => {
|
||||||
|
render(
|
||||||
|
<TagsInput
|
||||||
|
editable={false}
|
||||||
|
tags={mockTags}
|
||||||
|
onTagsUpdate={mockOnTagsUpdate}
|
||||||
|
/>,
|
||||||
|
{ wrapper: MemoryRouter }
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(TagsContainerV2).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
permission: false,
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles undefined tags prop', () => {
|
||||||
|
render(<TagsInput editable onTagsUpdate={mockOnTagsUpdate} />, {
|
||||||
|
wrapper: MemoryRouter,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(screen.getByTestId('tags-input-container')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -39,10 +39,6 @@ describe('ExpandableCard', () => {
|
|||||||
|
|
||||||
expect(screen.getByText('Test Card')).toBeInTheDocument();
|
expect(screen.getByText('Test Card')).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('test-content')).toBeInTheDocument();
|
expect(screen.getByTestId('test-content')).toBeInTheDocument();
|
||||||
expect(screen.getByRole('button')).toHaveAttribute(
|
|
||||||
'title',
|
|
||||||
'label.collapse'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders with custom data-testid', () => {
|
it('renders with custom data-testid', () => {
|
||||||
@ -104,7 +100,6 @@ describe('ExpandableCard', () => {
|
|||||||
const expandButton = screen.getByRole('button');
|
const expandButton = screen.getByRole('button');
|
||||||
|
|
||||||
// Initial state (collapsed)
|
// Initial state (collapsed)
|
||||||
expect(expandButton).toHaveAttribute('title', 'label.collapse');
|
|
||||||
expect(expandButton.closest('.ant-card')).toHaveClass('expanded');
|
expect(expandButton.closest('.ant-card')).toHaveClass('expanded');
|
||||||
|
|
||||||
// Click to collapse
|
// Click to collapse
|
||||||
@ -112,7 +107,6 @@ describe('ExpandableCard', () => {
|
|||||||
fireEvent.click(expandButton);
|
fireEvent.click(expandButton);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(expandButton).toHaveAttribute('title', 'label.expand');
|
|
||||||
expect(expandButton.closest('.ant-card')).not.toHaveClass('collapsed');
|
expect(expandButton.closest('.ant-card')).not.toHaveClass('collapsed');
|
||||||
|
|
||||||
// Click to expand again
|
// Click to expand again
|
||||||
@ -120,7 +114,6 @@ describe('ExpandableCard', () => {
|
|||||||
fireEvent.click(expandButton);
|
fireEvent.click(expandButton);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(expandButton).toHaveAttribute('title', 'label.collapse');
|
|
||||||
expect(expandButton.closest('.ant-card')).toHaveClass('expanded');
|
expect(expandButton.closest('.ant-card')).toHaveClass('expanded');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -239,8 +232,9 @@ describe('ExpandableCard', () => {
|
|||||||
fireEvent.click(expandButton);
|
fireEvent.click(expandButton);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Should not throw any errors
|
const card = screen.getByRole('button').closest('.ant-card');
|
||||||
expect(expandButton).toHaveAttribute('title', 'label.expand');
|
|
||||||
|
expect(card).not.toHaveClass('expanded');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('works with minimal cardProps', () => {
|
it('works with minimal cardProps', () => {
|
||||||
|
@ -44,6 +44,10 @@ const ExpandableCard = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
|
bodyStyle={{
|
||||||
|
// This will prevent the card body from having padding when there is no content
|
||||||
|
padding: children ? undefined : '0px',
|
||||||
|
}}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'new-header-border-card w-full',
|
'new-header-border-card w-full',
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* 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 Icon from '@ant-design/icons';
|
import Icon, { PlusOutlined } from '@ant-design/icons';
|
||||||
import { Button, ButtonProps, Tooltip } from 'antd';
|
import { Button, ButtonProps, Tooltip } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -129,17 +129,42 @@ export const AlignRightIconButton = ({
|
|||||||
export const CardExpandCollapseIconButton = ({
|
export const CardExpandCollapseIconButton = ({
|
||||||
title,
|
title,
|
||||||
className,
|
className,
|
||||||
size,
|
disabled,
|
||||||
...props
|
...props
|
||||||
}: IconButtonPropsInternal) => {
|
}: IconButtonPropsInternal) => {
|
||||||
return (
|
const button = (
|
||||||
<Button
|
<Button
|
||||||
className={classNames('bordered', className)}
|
className={classNames('bordered', className)}
|
||||||
|
disabled={disabled}
|
||||||
icon={<CardExpandCollapseIcon />}
|
icon={<CardExpandCollapseIcon />}
|
||||||
size={size}
|
|
||||||
title={title}
|
|
||||||
type="text"
|
type="text"
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip title={title}>
|
||||||
|
{/* Adding span to fix the issue with className is not being applied for disabled button
|
||||||
|
Refer this comment for more details https://github.com/ant-design/ant-design/issues/21404#issuecomment-586800984 */}
|
||||||
|
{disabled ? <span className={className}>{button}</span> : button}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PlusIconButton = ({
|
||||||
|
title,
|
||||||
|
className,
|
||||||
|
size,
|
||||||
|
...props
|
||||||
|
}: IconButtonPropsInternal) => {
|
||||||
|
return (
|
||||||
|
<Tooltip title={title}>
|
||||||
|
<Button
|
||||||
|
className={classNames('bordered', className)}
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
size={size}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -50,7 +50,7 @@ export const FrequentlyJoinedTables = () => {
|
|||||||
|
|
||||||
const content = joinedTables.map((table) => (
|
const content = joinedTables.map((table) => (
|
||||||
<Space
|
<Space
|
||||||
className="w-full frequently-joint-data justify-between"
|
className="w-full frequently-joint-data justify-between m-t-xss"
|
||||||
data-testid="related-tables-data"
|
data-testid="related-tables-data"
|
||||||
key={table.name}
|
key={table.name}
|
||||||
size={4}>
|
size={4}>
|
||||||
|
@ -16,10 +16,11 @@ import { isEmpty, map } from 'lodash';
|
|||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { ReactComponent as PlusIcon } from '../../../assets/svg/plus-primary.svg';
|
|
||||||
import ExpandableCard from '../../../components/common/ExpandableCard/ExpandableCard';
|
import ExpandableCard from '../../../components/common/ExpandableCard/ExpandableCard';
|
||||||
import { EditIconButton } from '../../../components/common/IconButtons/EditIconButton';
|
import {
|
||||||
import TagButton from '../../../components/common/TagButton/TagButton.component';
|
EditIconButton,
|
||||||
|
PlusIconButton,
|
||||||
|
} from '../../../components/common/IconButtons/EditIconButton';
|
||||||
import { useGenericContext } from '../../../components/Customization/GenericProvider/GenericProvider';
|
import { useGenericContext } from '../../../components/Customization/GenericProvider/GenericProvider';
|
||||||
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
|
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
|
||||||
import { EntityType, FqnPart } from '../../../enums/entity.enum';
|
import { EntityType, FqnPart } from '../../../enums/entity.enum';
|
||||||
@ -63,30 +64,29 @@ const TableConstraints = () => {
|
|||||||
{t('label.table-constraints')}
|
{t('label.table-constraints')}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
|
||||||
{hasPermission && !isEmpty(data?.tableConstraints) && (
|
{hasPermission &&
|
||||||
|
(isEmpty(data?.tableConstraints) ? (
|
||||||
|
<PlusIconButton
|
||||||
|
data-testid="table-constraints-add-button"
|
||||||
|
size="small"
|
||||||
|
title={t('label.add-entity', {
|
||||||
|
entity: t('label.table-constraints'),
|
||||||
|
})}
|
||||||
|
onClick={handleOpenEditConstraintModal}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<EditIconButton
|
<EditIconButton
|
||||||
newLook
|
newLook
|
||||||
data-testid="edit-table-constraint-button"
|
data-testid="edit-table-constraint-button"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={handleOpenEditConstraintModal}
|
onClick={handleOpenEditConstraintModal}
|
||||||
/>
|
/>
|
||||||
)}
|
))}
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
|
|
||||||
const content = (
|
const content = isEmpty(data?.tableConstraints) ? null : (
|
||||||
<Space className="w-full new-header-border-card" direction="vertical">
|
<Space className="w-full new-header-border-card" direction="vertical">
|
||||||
{hasPermission && isEmpty(data?.tableConstraints) && (
|
|
||||||
<TagButton
|
|
||||||
className="text-primary cursor-pointer"
|
|
||||||
dataTestId="table-constraints-add-button"
|
|
||||||
icon={<PlusIcon height={16} name="plus" width={16} />}
|
|
||||||
label={t('label.add')}
|
|
||||||
tooltip=""
|
|
||||||
onClick={handleOpenEditConstraintModal}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{data?.tableConstraints?.map(
|
{data?.tableConstraints?.map(
|
||||||
({ constraintType, columns, referredColumns }) => {
|
({ constraintType, columns, referredColumns }) => {
|
||||||
if (constraintType === ConstraintType.PrimaryKey) {
|
if (constraintType === ConstraintType.PrimaryKey) {
|
||||||
@ -163,6 +163,18 @@ const TableConstraints = () => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ExpandableCard
|
||||||
|
cardProps={{
|
||||||
|
title: header,
|
||||||
|
}}
|
||||||
|
isExpandDisabled={isEmpty(data?.tableConstraints)}>
|
||||||
|
{content}
|
||||||
|
</ExpandableCard>
|
||||||
{isModalOpen && (
|
{isModalOpen && (
|
||||||
<TableConstraintsModal
|
<TableConstraintsModal
|
||||||
constraint={data?.tableConstraints}
|
constraint={data?.tableConstraints}
|
||||||
@ -171,17 +183,7 @@ const TableConstraints = () => {
|
|||||||
onSave={handleSubmit}
|
onSave={handleSubmit}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</>
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ExpandableCard
|
|
||||||
cardProps={{
|
|
||||||
title: header,
|
|
||||||
}}
|
|
||||||
isExpandDisabled={isEmpty(data?.tableConstraints)}>
|
|
||||||
{content}
|
|
||||||
</ExpandableCard>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user