mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-07-14 12:38:45 +00:00
feat(ui): supported separate column for Pipeline entity (#11600)
* Supported separate column for Pipline entity * fix code smell and bug * fix code bug * fix code bug
This commit is contained in:
parent
2570ef7795
commit
1df322a704
@ -47,6 +47,7 @@ export const TAGS_ADD_REMOVE_ENTITIES = [
|
|||||||
serviceName: 'sample_airflow',
|
serviceName: 'sample_airflow',
|
||||||
fieldName: 'dim_address_task',
|
fieldName: 'dim_address_task',
|
||||||
tags: ['PersonalData.Personal', 'PII.Sensitive'],
|
tags: ['PersonalData.Personal', 'PII.Sensitive'],
|
||||||
|
separate: true,
|
||||||
},
|
},
|
||||||
// Todo: need to investigate on below test
|
// Todo: need to investigate on below test
|
||||||
// more details:- https://cloud.cypress.io/projects/a9yxci/runs/18306/test-results/abe5ab43-84c9-49da-b50f-4936bbcfdd3d
|
// more details:- https://cloud.cypress.io/projects/a9yxci/runs/18306/test-results/abe5ab43-84c9-49da-b50f-4936bbcfdd3d
|
||||||
|
@ -474,7 +474,7 @@ const DashboardDetails = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChartTagSelection = (
|
const handleChartTagSelection = async (
|
||||||
selectedTags: Array<EntityTags>,
|
selectedTags: Array<EntityTags>,
|
||||||
editColumnTag: ChartType,
|
editColumnTag: ChartType,
|
||||||
otherTags: TagLabel[]
|
otherTags: TagLabel[]
|
||||||
@ -505,7 +505,7 @@ const DashboardDetails = ({
|
|||||||
tags: [...(prevTags as TagLabel[]), ...newTags],
|
tags: [...(prevTags as TagLabel[]), ...newTags],
|
||||||
};
|
};
|
||||||
const jsonPatch = compare(editColumnTag, updatedChart);
|
const jsonPatch = compare(editColumnTag, updatedChart);
|
||||||
chartTagUpdateHandler(editColumnTag.id, jsonPatch);
|
await chartTagUpdateHandler(editColumnTag.id, jsonPatch);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -59,7 +59,10 @@ export interface DashboardDetailsProps {
|
|||||||
chartId: string,
|
chartId: string,
|
||||||
patch: Array<Operation>
|
patch: Array<Operation>
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
chartTagUpdateHandler: (chartId: string, patch: Array<Operation>) => void;
|
chartTagUpdateHandler: (
|
||||||
|
chartId: string,
|
||||||
|
patch: Array<Operation>
|
||||||
|
) => Promise<void>;
|
||||||
versionHandler: () => void;
|
versionHandler: () => void;
|
||||||
postFeedHandler: (value: string, id: string) => void;
|
postFeedHandler: (value: string, id: string) => void;
|
||||||
deletePostHandler: (
|
deletePostHandler: (
|
||||||
|
@ -105,11 +105,6 @@ const EntityTable = ({
|
|||||||
index: number;
|
index: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const [editColumnTag, setEditColumnTag] = useState<{
|
|
||||||
column: Column;
|
|
||||||
index: number;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
||||||
const [tagFetchFailed, setTagFetchFailed] = useState<boolean>(false);
|
const [tagFetchFailed, setTagFetchFailed] = useState<boolean>(false);
|
||||||
|
|
||||||
@ -228,14 +223,14 @@ const EntityTable = ({
|
|||||||
editColumn.column.fullyQualifiedName,
|
editColumn.column.fullyQualifiedName,
|
||||||
columnDescription
|
columnDescription
|
||||||
);
|
);
|
||||||
await onUpdate?.(tableCols);
|
await onUpdate(tableCols);
|
||||||
setEditColumn(undefined);
|
setEditColumn(undefined);
|
||||||
} else {
|
} else {
|
||||||
setEditColumn(undefined);
|
setEditColumn(undefined);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTagSelection = (
|
const handleTagSelection = async (
|
||||||
selectedTags: EntityTags[],
|
selectedTags: EntityTags[],
|
||||||
editColumnTag: Column,
|
editColumnTag: Column,
|
||||||
otherTags: TagLabel[]
|
otherTags: TagLabel[]
|
||||||
@ -247,9 +242,8 @@ const EntityTable = ({
|
|||||||
if (newSelectedTags && editColumnTag) {
|
if (newSelectedTags && editColumnTag) {
|
||||||
const tableCols = cloneDeep(tableColumns);
|
const tableCols = cloneDeep(tableColumns);
|
||||||
updateColumnTags(tableCols, editColumnTag.name, newSelectedTags);
|
updateColumnTags(tableCols, editColumnTag.name, newSelectedTags);
|
||||||
onUpdate?.(tableCols);
|
await onUpdate(tableCols);
|
||||||
}
|
}
|
||||||
setEditColumnTag(undefined);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const searchInColumns = (table: Column[], searchText: string): Column[] => {
|
const searchInColumns = (table: Column[], searchText: string): Column[] => {
|
||||||
@ -590,7 +584,6 @@ const EntityTable = ({
|
|||||||
entityFieldThreads,
|
entityFieldThreads,
|
||||||
entityFqn,
|
entityFqn,
|
||||||
tableConstraints,
|
tableConstraints,
|
||||||
editColumnTag,
|
|
||||||
isTagLoading,
|
isTagLoading,
|
||||||
handleUpdate,
|
handleUpdate,
|
||||||
handleTagSelection,
|
handleTagSelection,
|
||||||
|
@ -28,7 +28,7 @@ export interface EntityTableProps {
|
|||||||
entityFqn?: string;
|
entityFqn?: string;
|
||||||
entityFieldThreads?: EntityFieldThreads[];
|
entityFieldThreads?: EntityFieldThreads[];
|
||||||
entityFieldTasks?: EntityFieldThreads[];
|
entityFieldTasks?: EntityFieldThreads[];
|
||||||
onUpdate?: (columns: Column[]) => Promise<void>;
|
onUpdate: (columns: Column[]) => Promise<void>;
|
||||||
onThreadLinkSelect?: (value: string, threadType?: ThreadType) => void;
|
onThreadLinkSelect?: (value: string, threadType?: ThreadType) => void;
|
||||||
onEntityFieldSelect?: (value: string) => void;
|
onEntityFieldSelect?: (value: string) => void;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import EntityTableV1 from './EntityTable.component';
|
|||||||
|
|
||||||
const onEntityFieldSelect = jest.fn();
|
const onEntityFieldSelect = jest.fn();
|
||||||
const onThreadLinkSelect = jest.fn();
|
const onThreadLinkSelect = jest.fn();
|
||||||
|
const onUpdate = jest.fn();
|
||||||
|
|
||||||
const mockTableConstraints = [
|
const mockTableConstraints = [
|
||||||
{
|
{
|
||||||
@ -114,6 +115,7 @@ const mockEntityTableProp = {
|
|||||||
tableConstraints: mockTableConstraints,
|
tableConstraints: mockTableConstraints,
|
||||||
onEntityFieldSelect,
|
onEntityFieldSelect,
|
||||||
onThreadLinkSelect,
|
onThreadLinkSelect,
|
||||||
|
onUpdate,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockTagList = [
|
const mockTagList = [
|
||||||
|
@ -27,8 +27,10 @@ import { ReactComponent as EditIcon } from 'assets/svg/edit-new.svg';
|
|||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { ActivityFilters } from 'components/ActivityFeed/ActivityFeedList/ActivityFeedList.interface';
|
import { ActivityFilters } from 'components/ActivityFeed/ActivityFeedList/ActivityFeedList.interface';
|
||||||
import { EntityName } from 'components/Modals/EntityNameModal/EntityNameModal.interface';
|
import { EntityName } from 'components/Modals/EntityNameModal/EntityNameModal.interface';
|
||||||
|
import TableTags from 'components/TableTags/TableTags.component';
|
||||||
import { compare, Operation } from 'fast-json-patch';
|
import { compare, Operation } from 'fast-json-patch';
|
||||||
import { isEmpty, isUndefined } from 'lodash';
|
import { TagSource } from 'generated/type/schema';
|
||||||
|
import { isEmpty, isUndefined, map } from 'lodash';
|
||||||
import { EntityTags, ExtraInfo, TagOption } from 'Models';
|
import { EntityTags, ExtraInfo, TagOption } from 'Models';
|
||||||
import React, {
|
import React, {
|
||||||
RefObject,
|
RefObject,
|
||||||
@ -41,6 +43,8 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { Link, Redirect, useHistory, useParams } from 'react-router-dom';
|
import { Link, Redirect, useHistory, useParams } from 'react-router-dom';
|
||||||
import { getAllFeeds, postFeedById, postThread } from 'rest/feedsAPI';
|
import { getAllFeeds, postFeedById, postThread } from 'rest/feedsAPI';
|
||||||
import { restorePipeline } from 'rest/pipelineAPI';
|
import { restorePipeline } from 'rest/pipelineAPI';
|
||||||
|
import { fetchGlossaryTerms, getGlossaryTermlist } from 'utils/GlossaryUtils';
|
||||||
|
import { getFilterTags } from 'utils/TableTags/TableTags.utils';
|
||||||
import AppState from '../../AppState';
|
import AppState from '../../AppState';
|
||||||
import { ReactComponent as ExternalLinkIcon } from '../../assets/svg/external-link.svg';
|
import { ReactComponent as ExternalLinkIcon } from '../../assets/svg/external-link.svg';
|
||||||
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
|
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
|
||||||
@ -87,7 +91,7 @@ import {
|
|||||||
} from '../../utils/FeedUtils';
|
} from '../../utils/FeedUtils';
|
||||||
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
||||||
import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils';
|
import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils';
|
||||||
import { fetchTagsAndGlossaryTerms } from '../../utils/TagsUtils';
|
import { getClassifications, getTaglist } from '../../utils/TagsUtils';
|
||||||
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
|
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
|
||||||
import ActivityFeedList from '../ActivityFeed/ActivityFeedList/ActivityFeedList';
|
import ActivityFeedList from '../ActivityFeed/ActivityFeedList/ActivityFeedList';
|
||||||
import ActivityThreadPanel from '../ActivityFeed/ActivityThreadPanel/ActivityThreadPanel';
|
import ActivityThreadPanel from '../ActivityFeed/ActivityThreadPanel/ActivityThreadPanel';
|
||||||
@ -103,8 +107,6 @@ import Loader from '../Loader/Loader';
|
|||||||
import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor';
|
import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor';
|
||||||
import { usePermissionProvider } from '../PermissionProvider/PermissionProvider';
|
import { usePermissionProvider } from '../PermissionProvider/PermissionProvider';
|
||||||
import { ResourceEntity } from '../PermissionProvider/PermissionProvider.interface';
|
import { ResourceEntity } from '../PermissionProvider/PermissionProvider.interface';
|
||||||
import TagsContainer from '../Tag/TagsContainer/tags-container';
|
|
||||||
import TagsViewer from '../Tag/TagsViewer/tags-viewer';
|
|
||||||
import TasksDAGView from '../TasksDAGView/TasksDAGView';
|
import TasksDAGView from '../TasksDAGView/TasksDAGView';
|
||||||
import { PipeLineDetailsProp } from './PipelineDetails.interface';
|
import { PipeLineDetailsProp } from './PipelineDetails.interface';
|
||||||
|
|
||||||
@ -150,10 +152,7 @@ const PipelineDetails = ({
|
|||||||
}, [pipelineDetails]);
|
}, [pipelineDetails]);
|
||||||
|
|
||||||
// local state variables
|
// local state variables
|
||||||
const [editTaskTags, setEditTaskTags] = useState<{
|
|
||||||
task: Task;
|
|
||||||
index: number;
|
|
||||||
}>();
|
|
||||||
const [isEdit, setIsEdit] = useState(false);
|
const [isEdit, setIsEdit] = useState(false);
|
||||||
const [followersCount, setFollowersCount] = useState(0);
|
const [followersCount, setFollowersCount] = useState(0);
|
||||||
const [isFollowing, setIsFollowing] = useState(false);
|
const [isFollowing, setIsFollowing] = useState(false);
|
||||||
@ -175,8 +174,6 @@ const PipelineDetails = ({
|
|||||||
EntityFieldThreadCount[]
|
EntityFieldThreadCount[]
|
||||||
>([]);
|
>([]);
|
||||||
|
|
||||||
const [tagList, setTagList] = useState<TagOption[]>();
|
|
||||||
|
|
||||||
const [threadLink, setThreadLink] = useState<string>('');
|
const [threadLink, setThreadLink] = useState<string>('');
|
||||||
|
|
||||||
const [elementRef, isInView] = useElementInView(observerOptions);
|
const [elementRef, isInView] = useElementInView(observerOptions);
|
||||||
@ -195,6 +192,11 @@ const PipelineDetails = ({
|
|||||||
|
|
||||||
const [activityFilter, setActivityFilter] = useState<ActivityFilters>();
|
const [activityFilter, setActivityFilter] = useState<ActivityFilters>();
|
||||||
|
|
||||||
|
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
||||||
|
const [tagFetchFailed, setTagFetchFailed] = useState<boolean>(false);
|
||||||
|
const [glossaryTags, setGlossaryTags] = useState<TagOption[]>([]);
|
||||||
|
const [classificationTags, setClassificationTags] = useState<TagOption[]>([]);
|
||||||
|
|
||||||
// local state ends
|
// local state ends
|
||||||
|
|
||||||
const USERId = getCurrentUserId();
|
const USERId = getCurrentUserId();
|
||||||
@ -213,6 +215,11 @@ const PipelineDetails = ({
|
|||||||
[entityThreadLoading]
|
[entityThreadLoading]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const hasTagEditAccess = useMemo(
|
||||||
|
() => pipelinePermissions.EditAll || pipelinePermissions.EditTags,
|
||||||
|
[pipelinePermissions]
|
||||||
|
);
|
||||||
|
|
||||||
const getEntityFeedCount = () => {
|
const getEntityFeedCount = () => {
|
||||||
getFeedCounts(
|
getFeedCounts(
|
||||||
EntityType.PIPELINE,
|
EntityType.PIPELINE,
|
||||||
@ -239,6 +246,41 @@ const PipelineDetails = ({
|
|||||||
}
|
}
|
||||||
}, [pipelineDetails.id, getEntityPermission, setPipelinePermissions]);
|
}, [pipelineDetails.id, getEntityPermission, setPipelinePermissions]);
|
||||||
|
|
||||||
|
const fetchGlossaryTags = async () => {
|
||||||
|
setIsTagLoading(true);
|
||||||
|
try {
|
||||||
|
const res = await fetchGlossaryTerms();
|
||||||
|
|
||||||
|
const glossaryTerms: TagOption[] = getGlossaryTermlist(res).map(
|
||||||
|
(tag) => ({ fqn: tag, source: TagSource.Glossary })
|
||||||
|
);
|
||||||
|
setGlossaryTags(glossaryTerms);
|
||||||
|
} catch {
|
||||||
|
setTagFetchFailed(true);
|
||||||
|
} finally {
|
||||||
|
setIsTagLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchClassificationTags = async () => {
|
||||||
|
setIsTagLoading(true);
|
||||||
|
try {
|
||||||
|
const res = await getClassifications();
|
||||||
|
const tagList = await getTaglist(res.data);
|
||||||
|
|
||||||
|
const classificationTag: TagOption[] = map(tagList, (tag) => ({
|
||||||
|
fqn: tag,
|
||||||
|
source: TagSource.Classification,
|
||||||
|
}));
|
||||||
|
|
||||||
|
setClassificationTags(classificationTag);
|
||||||
|
} catch {
|
||||||
|
setTagFetchFailed(true);
|
||||||
|
} finally {
|
||||||
|
setIsTagLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (pipelineDetails.id) {
|
if (pipelineDetails.id) {
|
||||||
fetchResourcePermission();
|
fetchResourcePermission();
|
||||||
@ -497,105 +539,47 @@ const PipelineDetails = ({
|
|||||||
getFeedData(undefined, feedFilter, threadType);
|
getFeedData(undefined, feedFilter, threadType);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleEditTaskTag = (task: Task, index: number): void => {
|
const handleTableTagSelection = async (
|
||||||
setEditTaskTags({ task: { ...task, tags: [] }, index });
|
selectedTags: EntityTags[],
|
||||||
};
|
editColumnTag: Task,
|
||||||
|
otherTags: TagLabel[]
|
||||||
const handleTableTagSelection = (
|
|
||||||
selectedTags: Array<EntityTags> = [],
|
|
||||||
task: {
|
|
||||||
task: Task;
|
|
||||||
index: number;
|
|
||||||
}
|
|
||||||
) => {
|
) => {
|
||||||
const selectedTask = isUndefined(editTask) ? task : editTask;
|
const newSelectedTags: TagOption[] = map(
|
||||||
const prevTags = selectedTask.task.tags?.filter((tag) =>
|
[...selectedTags, ...otherTags],
|
||||||
selectedTags.some((selectedTag) => selectedTag.tagFQN === tag.tagFQN)
|
(tag) => ({ fqn: tag.tagFQN, source: tag.source })
|
||||||
);
|
);
|
||||||
|
|
||||||
const newTags = selectedTags
|
const prevTags = editColumnTag.tags?.filter((tag) =>
|
||||||
|
newSelectedTags.some((selectedTag) => selectedTag.fqn === tag.tagFQN)
|
||||||
|
);
|
||||||
|
|
||||||
|
const newTags = newSelectedTags
|
||||||
.filter(
|
.filter(
|
||||||
(selectedTag) =>
|
(selectedTag) =>
|
||||||
!selectedTask.task.tags?.some(
|
!editColumnTag.tags?.some((tag) => tag.tagFQN === selectedTag.fqn)
|
||||||
(tag) => tag.tagFQN === selectedTag.tagFQN
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.map((tag) => ({
|
.map((tag) => ({
|
||||||
labelType: 'Manual',
|
labelType: 'Manual',
|
||||||
state: 'Confirmed',
|
state: 'Confirmed',
|
||||||
source: tag.source,
|
source: tag.source,
|
||||||
tagFQN: tag.tagFQN,
|
tagFQN: tag.fqn,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const updatedTasks: Task[] = [...(pipelineDetails.tasks || [])];
|
|
||||||
|
|
||||||
const updatedTask = {
|
const updatedTask = {
|
||||||
...selectedTask.task,
|
...editColumnTag,
|
||||||
tags: [...(prevTags as TagLabel[]), ...newTags],
|
tags: [...(prevTags as TagLabel[]), ...newTags],
|
||||||
} as Task;
|
} as Task;
|
||||||
|
|
||||||
updatedTasks[selectedTask.index] = updatedTask;
|
const updatedTasks: Task[] = [...(pipelineDetails.tasks ?? [])].map(
|
||||||
|
(task) => (task.name === editColumnTag.name ? updatedTask : task)
|
||||||
|
);
|
||||||
|
|
||||||
const updatedPipeline = { ...pipelineDetails, tasks: updatedTasks };
|
const updatedPipeline = { ...pipelineDetails, tasks: updatedTasks };
|
||||||
const jsonPatch = compare(pipelineDetails, updatedPipeline);
|
const jsonPatch = compare(pipelineDetails, updatedPipeline);
|
||||||
|
|
||||||
taskUpdateHandler(jsonPatch);
|
await taskUpdateHandler(jsonPatch);
|
||||||
setEditTaskTags(undefined);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useMemo(() => {
|
|
||||||
fetchTagsAndGlossaryTerms().then((response) => {
|
|
||||||
setTagList(response);
|
|
||||||
});
|
|
||||||
}, [setTagList]);
|
|
||||||
|
|
||||||
const addButtonHandler = useCallback((record, index) => {
|
|
||||||
handleEditTaskTag(record, index);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const renderTags = useCallback(
|
|
||||||
(tags, record, index) => (
|
|
||||||
<div className="relative tableBody-cell" data-testid="tags-wrapper">
|
|
||||||
{deleted ? (
|
|
||||||
<TagsViewer sizeCap={-1} tags={tags || []} />
|
|
||||||
) : (
|
|
||||||
<TagsContainer
|
|
||||||
editable={editTaskTags?.index === index}
|
|
||||||
selectedTags={tags as EntityTags[]}
|
|
||||||
showAddTagButton={
|
|
||||||
(pipelinePermissions.EditAll || pipelinePermissions.EditTags) &&
|
|
||||||
isEmpty(tags)
|
|
||||||
}
|
|
||||||
showEditTagButton={
|
|
||||||
pipelinePermissions.EditAll || pipelinePermissions.EditTags
|
|
||||||
}
|
|
||||||
size="small"
|
|
||||||
tagList={tagList ?? []}
|
|
||||||
type="label"
|
|
||||||
onAddButtonClick={() => addButtonHandler(record, index)}
|
|
||||||
onCancel={() => {
|
|
||||||
setEditTask(undefined);
|
|
||||||
}}
|
|
||||||
onEditButtonClick={() => addButtonHandler(record, index)}
|
|
||||||
onSelectionChange={(tags) => {
|
|
||||||
handleTableTagSelection(tags, {
|
|
||||||
task: record,
|
|
||||||
index: index,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
[
|
|
||||||
tagList,
|
|
||||||
editTaskTags,
|
|
||||||
pipelinePermissions.EditAll,
|
|
||||||
pipelinePermissions.EditTags,
|
|
||||||
deleted,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
const taskColumns: ColumnsType<Task> = useMemo(
|
const taskColumns: ColumnsType<Task> = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@ -669,14 +653,65 @@ const PipelineDetails = ({
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: t('label.tag-plural'),
|
|
||||||
dataIndex: 'tags',
|
|
||||||
title: t('label.tag-plural'),
|
title: t('label.tag-plural'),
|
||||||
width: 350,
|
dataIndex: 'tags',
|
||||||
render: renderTags,
|
key: 'tags',
|
||||||
|
accessor: 'tags',
|
||||||
|
width: 300,
|
||||||
|
render: (tags, record, index) => (
|
||||||
|
<TableTags<Task>
|
||||||
|
dataTestId="classification-tags"
|
||||||
|
fetchTags={fetchClassificationTags}
|
||||||
|
handleTagSelection={handleTableTagSelection}
|
||||||
|
hasTagEditAccess={hasTagEditAccess}
|
||||||
|
index={index}
|
||||||
|
isReadOnly={deleted}
|
||||||
|
isTagLoading={isTagLoading}
|
||||||
|
record={record}
|
||||||
|
tagFetchFailed={tagFetchFailed}
|
||||||
|
tagList={classificationTags}
|
||||||
|
tags={getFilterTags(tags)}
|
||||||
|
type={TagSource.Classification}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('label.glossary-term-plural'),
|
||||||
|
dataIndex: 'tags',
|
||||||
|
key: 'tags',
|
||||||
|
accessor: 'tags',
|
||||||
|
width: 300,
|
||||||
|
render: (tags, record, index) => (
|
||||||
|
<TableTags<Task>
|
||||||
|
dataTestId="glossary-tags"
|
||||||
|
fetchTags={fetchGlossaryTags}
|
||||||
|
handleTagSelection={handleTableTagSelection}
|
||||||
|
hasTagEditAccess={hasTagEditAccess}
|
||||||
|
index={index}
|
||||||
|
isReadOnly={deleted}
|
||||||
|
isTagLoading={isTagLoading}
|
||||||
|
record={record}
|
||||||
|
tagFetchFailed={tagFetchFailed}
|
||||||
|
tagList={glossaryTags}
|
||||||
|
tags={getFilterTags(tags)}
|
||||||
|
type={TagSource.Glossary}
|
||||||
|
/>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[pipelinePermissions, editTask, editTaskTags, tagList, deleted]
|
[
|
||||||
|
fetchGlossaryTags,
|
||||||
|
fetchClassificationTags,
|
||||||
|
handleTableTagSelection,
|
||||||
|
classificationTags,
|
||||||
|
hasTagEditAccess,
|
||||||
|
pipelinePermissions,
|
||||||
|
editTask,
|
||||||
|
deleted,
|
||||||
|
isTagLoading,
|
||||||
|
tagFetchFailed,
|
||||||
|
glossaryTags,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
findAllByTestId,
|
||||||
findByTestId,
|
findByTestId,
|
||||||
findByText,
|
findByText,
|
||||||
fireEvent,
|
fireEvent,
|
||||||
@ -19,7 +20,6 @@ import {
|
|||||||
render,
|
render,
|
||||||
screen,
|
screen,
|
||||||
} from '@testing-library/react';
|
} from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { act } from 'react-test-renderer';
|
import { act } from 'react-test-renderer';
|
||||||
@ -81,17 +81,6 @@ const mockTasks = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const mockTags = [
|
|
||||||
{
|
|
||||||
tagFQN: 'PII.Sensitive',
|
|
||||||
source: 'Tag',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tagFQN: 'PersonalData.Personal',
|
|
||||||
source: 'Tag',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const mockTaskUpdateHandler = jest.fn();
|
const mockTaskUpdateHandler = jest.fn();
|
||||||
|
|
||||||
const PipelineDetailsProps = {
|
const PipelineDetailsProps = {
|
||||||
@ -147,14 +136,6 @@ jest.mock('../common/rich-text-editor/RichTextEditorPreviewer', () => {
|
|||||||
return jest.fn().mockReturnValue(<p>RichTextEditorPreviwer</p>);
|
return jest.fn().mockReturnValue(<p>RichTextEditorPreviwer</p>);
|
||||||
});
|
});
|
||||||
|
|
||||||
jest.mock('components/Tag/TagsContainer/tags-container', () => {
|
|
||||||
return jest.fn().mockReturnValue(<p>Tag Container</p>);
|
|
||||||
});
|
|
||||||
|
|
||||||
jest.mock('components/Tag/Tags/tags', () => {
|
|
||||||
return jest.fn().mockReturnValue(<p>Tags</p>);
|
|
||||||
});
|
|
||||||
|
|
||||||
jest.mock('../EntityLineage/EntityLineage.component', () => {
|
jest.mock('../EntityLineage/EntityLineage.component', () => {
|
||||||
return jest.fn().mockReturnValue(<p>EntityLineage</p>);
|
return jest.fn().mockReturnValue(<p>EntityLineage</p>);
|
||||||
});
|
});
|
||||||
@ -201,16 +182,11 @@ jest.mock('../../utils/CommonUtils', () => ({
|
|||||||
jest.mock('../Execution/Execution.component', () => {
|
jest.mock('../Execution/Execution.component', () => {
|
||||||
return jest.fn().mockImplementation(() => <p>Executions</p>);
|
return jest.fn().mockImplementation(() => <p>Executions</p>);
|
||||||
});
|
});
|
||||||
|
jest.mock('components/TableTags/TableTags.component', () =>
|
||||||
jest.mock('../Tag/TagsContainer/tags-container', () =>
|
jest
|
||||||
jest.fn().mockImplementation(({ onSelectionChange }) => (
|
.fn()
|
||||||
<div data-testid="tags-container">
|
.mockImplementation(() => (
|
||||||
<div
|
<div data-testid="table-tag-container">Table Tag Container</div>
|
||||||
data-testid="onSelectionChange"
|
|
||||||
onClick={() => onSelectionChange(mockTags)}>
|
|
||||||
onSelectionChange
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -235,6 +211,10 @@ describe('Test PipelineDetails component', () => {
|
|||||||
container,
|
container,
|
||||||
'label.custom-property-plural'
|
'label.custom-property-plural'
|
||||||
);
|
);
|
||||||
|
const tagsContainer = await findAllByTestId(
|
||||||
|
container,
|
||||||
|
'table-tag-container'
|
||||||
|
);
|
||||||
|
|
||||||
expect(EntityPageInfo).toBeInTheDocument();
|
expect(EntityPageInfo).toBeInTheDocument();
|
||||||
expect(description).toBeInTheDocument();
|
expect(description).toBeInTheDocument();
|
||||||
@ -243,6 +223,7 @@ describe('Test PipelineDetails component', () => {
|
|||||||
expect(lineageTab).toBeInTheDocument();
|
expect(lineageTab).toBeInTheDocument();
|
||||||
expect(executionsTab).toBeInTheDocument();
|
expect(executionsTab).toBeInTheDocument();
|
||||||
expect(customPropertiesTab).toBeInTheDocument();
|
expect(customPropertiesTab).toBeInTheDocument();
|
||||||
|
expect(tagsContainer).toHaveLength(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Check if active tab is tasks', async () => {
|
it('Check if active tab is tasks', async () => {
|
||||||
@ -381,22 +362,4 @@ describe('Test PipelineDetails component', () => {
|
|||||||
|
|
||||||
expect(obServerElement).toBeInTheDocument();
|
expect(obServerElement).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('taskUpdateHandler should be called after the tags are added or removed to a task', async () => {
|
|
||||||
render(<PipelineDetails {...PipelineDetailsProps} />, {
|
|
||||||
wrapper: MemoryRouter,
|
|
||||||
});
|
|
||||||
|
|
||||||
const tagsContainer = screen.getAllByTestId('tags-container');
|
|
||||||
|
|
||||||
expect(tagsContainer).toHaveLength(2);
|
|
||||||
|
|
||||||
const onSelectionChange = screen.getAllByTestId('onSelectionChange');
|
|
||||||
|
|
||||||
expect(onSelectionChange).toHaveLength(2);
|
|
||||||
|
|
||||||
await act(async () => userEvent.click(onSelectionChange[0]));
|
|
||||||
|
|
||||||
expect(mockTaskUpdateHandler).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -33,5 +33,5 @@ export type Props = {
|
|||||||
entityFieldTasks?: EntityFieldThreads[];
|
entityFieldTasks?: EntityFieldThreads[];
|
||||||
onThreadLinkSelect?: (value: string, threadType?: ThreadType) => void;
|
onThreadLinkSelect?: (value: string, threadType?: ThreadType) => void;
|
||||||
onEntityFieldSelect?: (value: string) => void;
|
onEntityFieldSelect?: (value: string) => void;
|
||||||
onUpdate?: (columns: Table['columns']) => Promise<void>;
|
onUpdate: (columns: Table['columns']) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
@ -140,8 +140,8 @@ const TableTags = <T extends TableUnion>({
|
|||||||
type="label"
|
type="label"
|
||||||
onAddButtonClick={addButtonHandler}
|
onAddButtonClick={addButtonHandler}
|
||||||
onCancel={() => setIsEdit(false)}
|
onCancel={() => setIsEdit(false)}
|
||||||
onSelectionChange={(selectedTags) => {
|
onSelectionChange={async (selectedTags) => {
|
||||||
handleTagSelection(selectedTags, record, otherTags);
|
await handleTagSelection(selectedTags, record, otherTags);
|
||||||
setIsEdit(false);
|
setIsEdit(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Task } from 'generated/entity/data/pipeline';
|
||||||
import { Field } from 'generated/entity/data/topic';
|
import { Field } from 'generated/entity/data/topic';
|
||||||
import { TagLabel, TagSource } from 'generated/type/tagLabel';
|
import { TagLabel, TagSource } from 'generated/type/tagLabel';
|
||||||
import { EntityTags, TagOption } from 'Models';
|
import { EntityTags, TagOption } from 'Models';
|
||||||
@ -33,7 +34,7 @@ export interface TableTagsComponentProps<T> {
|
|||||||
selectedTags: Array<EntityTags>,
|
selectedTags: Array<EntityTags>,
|
||||||
editColumnTag: T,
|
editColumnTag: T,
|
||||||
otherTags: TagLabel[]
|
otherTags: TagLabel[]
|
||||||
) => void;
|
) => Promise<void>;
|
||||||
onRequestTagsHandler?: (cell: T) => void;
|
onRequestTagsHandler?: (cell: T) => void;
|
||||||
getColumnName?: (cell: T) => string;
|
getColumnName?: (cell: T) => string;
|
||||||
getColumnFieldFQN?: string;
|
getColumnFieldFQN?: string;
|
||||||
@ -42,7 +43,7 @@ export interface TableTagsComponentProps<T> {
|
|||||||
entityFieldThreads?: EntityFieldThreads[];
|
entityFieldThreads?: EntityFieldThreads[];
|
||||||
tagFetchFailed: boolean;
|
tagFetchFailed: boolean;
|
||||||
type: TagSource;
|
type: TagSource;
|
||||||
fetchTags: () => void;
|
fetchTags: () => Promise<void>;
|
||||||
dataTestId: string;
|
dataTestId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,4 +57,4 @@ export interface TableTagsProps {
|
|||||||
Glossary: TagLabel[];
|
Glossary: TagLabel[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TableUnion = Column | Field | ChartType;
|
export type TableUnion = Column | Field | Task | ChartType;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user