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:
Ashish Gupta 2023-05-16 19:05:22 +05:30 committed by GitHub
parent 2570ef7795
commit 1df322a704
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 159 additions and 161 deletions

View File

@ -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

View File

@ -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);
} }
}; };

View File

@ -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: (

View File

@ -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,

View File

@ -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;
} }

View File

@ -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 = [

View File

@ -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(() => {

View File

@ -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,17 +182,12 @@ 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>
))
); );
describe('Test PipelineDetails component', () => { describe('Test PipelineDetails component', () => {
@ -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);
});
}); });

View File

@ -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>;
}; };

View File

@ -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);
}} }}
/> />

View File

@ -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;