mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-26 01:46:26 +00:00
✨ Activity Feed: Show "Request Tag" button for table column Tags (#6039)
* ✨ Activity Feed: Show "Request Tag" button for table column Tags
* Add support for task icon
* Addressing review comment
* Fix unit tests
* Fix unit test
* Fix explore page issue
This commit is contained in:
parent
954635b6dc
commit
c40e9ff685
@ -15,7 +15,7 @@ import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { Popover } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { cloneDeep, isNil, isUndefined, lowerCase } from 'lodash';
|
||||
import { cloneDeep, isEmpty, isNil, isUndefined, lowerCase } from 'lodash';
|
||||
import { EntityFieldThreads, EntityTags, TagOption } from 'Models';
|
||||
import React, { Fragment, useEffect, useState } from 'react';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
@ -28,10 +28,8 @@ import { SettledStatus } from '../../enums/axios.enum';
|
||||
import { EntityType, FqnPart } from '../../enums/entity.enum';
|
||||
import {
|
||||
Column,
|
||||
ColumnJoins,
|
||||
ColumnTest,
|
||||
JoinedWith,
|
||||
Table,
|
||||
} from '../../generated/entity/data/table';
|
||||
import { ThreadType } from '../../generated/entity/feed/thread';
|
||||
import { Operation } from '../../generated/entity/policies/accessControl/rule';
|
||||
@ -59,6 +57,7 @@ import {
|
||||
import { getTagCategories, getTaglist } from '../../utils/TagsUtils';
|
||||
import {
|
||||
getRequestDescriptionPath,
|
||||
getRequestTagsPath,
|
||||
getUpdateDescriptionPath,
|
||||
} from '../../utils/TasksUtils';
|
||||
import NonAdminAction from '../common/non-admin-action/NonAdminAction';
|
||||
@ -67,23 +66,8 @@ import RichTextEditorPreviewer from '../common/rich-text-editor/RichTextEditorPr
|
||||
import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor';
|
||||
import TagsContainer from '../tags-container/tags-container';
|
||||
import TagsViewer from '../tags-viewer/tags-viewer';
|
||||
|
||||
interface Props {
|
||||
owner: Table['owner'];
|
||||
tableColumns: ModifiedTableColumn[];
|
||||
joins: Array<ColumnJoins>;
|
||||
columnName: string;
|
||||
hasEditAccess: boolean;
|
||||
tableConstraints: Table['tableConstraints'];
|
||||
searchText?: string;
|
||||
isReadOnly?: boolean;
|
||||
entityFqn?: string;
|
||||
entityFieldThreads?: EntityFieldThreads[];
|
||||
entityFieldTasks?: EntityFieldThreads[];
|
||||
onUpdate?: (columns: ModifiedTableColumn[]) => void;
|
||||
onThreadLinkSelect?: (value: string, threadType?: ThreadType) => void;
|
||||
onEntityFieldSelect?: (value: string) => void;
|
||||
}
|
||||
import { TABLE_HEADERS } from './EntityTable.constant';
|
||||
import { EntityTableProps } from './EntityTable.interface';
|
||||
|
||||
const EntityTable = ({
|
||||
tableColumns,
|
||||
@ -98,35 +82,11 @@ const EntityTable = ({
|
||||
entityFqn,
|
||||
tableConstraints,
|
||||
entityFieldTasks,
|
||||
}: Props) => {
|
||||
}: EntityTableProps) => {
|
||||
const { isAdminUser, userPermissions } = useAuth();
|
||||
const { isAuthDisabled } = useAuthContext();
|
||||
const history = useHistory();
|
||||
const columns = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: 'Name',
|
||||
accessor: 'name',
|
||||
},
|
||||
{
|
||||
Header: 'Type',
|
||||
accessor: 'dataTypeDisplay',
|
||||
},
|
||||
{
|
||||
Header: 'Data Quality',
|
||||
accessor: 'columnTests',
|
||||
},
|
||||
{
|
||||
Header: 'Description',
|
||||
accessor: 'description',
|
||||
},
|
||||
{
|
||||
Header: 'Tags',
|
||||
accessor: 'tags',
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
const columns = TABLE_HEADERS;
|
||||
|
||||
const [searchedColumns, setSearchedColumns] = useState<ModifiedTableColumn[]>(
|
||||
[]
|
||||
@ -405,6 +365,15 @@ const EntityTable = ({
|
||||
);
|
||||
};
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
const onRequestTagsHandler = (cell: any) => {
|
||||
const field = EntityField.COLUMNS;
|
||||
const value = getColumnName(cell);
|
||||
history.push(
|
||||
getRequestTagsPath(EntityType.TABLE, entityFqn as string, field, value)
|
||||
);
|
||||
};
|
||||
|
||||
const prepareConstraintIcon = (
|
||||
columnName: string,
|
||||
columnConstraint?: string
|
||||
@ -460,6 +429,27 @@ const EntityTable = ({
|
||||
);
|
||||
};
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
const getRequestTagsElement = (cell: any) => {
|
||||
const hasTags = !isEmpty(cell.value || []);
|
||||
|
||||
return !hasTags ? (
|
||||
<button
|
||||
className="tw-w-8 tw-h-8 tw-mr-1 tw-flex-none link-text focus:tw-outline-none tw-opacity-0 group-hover:tw-opacity-100 tw-align-top"
|
||||
data-testid="request-tags"
|
||||
onClick={() => onRequestTagsHandler(cell)}>
|
||||
<Popover
|
||||
destroyTooltipOnHide
|
||||
content="Request tags"
|
||||
overlayClassName="ant-popover-request-description"
|
||||
trigger="hover"
|
||||
zIndex={9999}>
|
||||
<SVGIcons alt="request-tags" icon={Icons.REQUEST} width="16px" />
|
||||
</Popover>
|
||||
</button>
|
||||
) : null;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!searchText) {
|
||||
setSearchedColumns(tableColumns);
|
||||
@ -677,6 +667,7 @@ const EntityTable = ({
|
||||
/>
|
||||
</NonAdminAction>
|
||||
<div className="tw-mt-1">
|
||||
{getRequestTagsElement(cell)}
|
||||
{getFieldThreadElement(
|
||||
getColumnName(cell),
|
||||
'tags',
|
||||
@ -689,6 +680,23 @@ const EntityTable = ({
|
||||
)}${ENTITY_LINK_SEPARATOR}tags`,
|
||||
Boolean(cell.value.length)
|
||||
)}
|
||||
{getFieldThreadElement(
|
||||
getColumnName(cell),
|
||||
EntityField.TAGS,
|
||||
entityFieldTasks as EntityFieldThreads[],
|
||||
onThreadLinkSelect,
|
||||
EntityType.TABLE,
|
||||
entityFqn,
|
||||
`${
|
||||
EntityField.COLUMNS
|
||||
}${ENTITY_LINK_SEPARATOR}${getColumnName(
|
||||
cell
|
||||
)}${ENTITY_LINK_SEPARATOR}${
|
||||
EntityField.TAGS
|
||||
}`,
|
||||
Boolean(cell.value),
|
||||
ThreadType.Task
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
export const TABLE_HEADERS = [
|
||||
{
|
||||
Header: 'Name',
|
||||
accessor: 'name',
|
||||
},
|
||||
{
|
||||
Header: 'Type',
|
||||
accessor: 'dataTypeDisplay',
|
||||
},
|
||||
{
|
||||
Header: 'Data Quality',
|
||||
accessor: 'columnTests',
|
||||
},
|
||||
{
|
||||
Header: 'Description',
|
||||
accessor: 'description',
|
||||
},
|
||||
{
|
||||
Header: 'Tags',
|
||||
accessor: 'tags',
|
||||
},
|
||||
];
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2021 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 { EntityFieldThreads } from 'Models';
|
||||
import { ThreadType } from '../../generated/api/feed/createThread';
|
||||
import { ColumnJoins, Table } from '../../generated/entity/data/table';
|
||||
import { ModifiedTableColumn } from '../../interface/dataQuality.interface';
|
||||
|
||||
export interface EntityTableProps {
|
||||
owner: Table['owner'];
|
||||
tableColumns: ModifiedTableColumn[];
|
||||
joins: Array<ColumnJoins>;
|
||||
columnName: string;
|
||||
hasEditAccess: boolean;
|
||||
tableConstraints: Table['tableConstraints'];
|
||||
searchText?: string;
|
||||
isReadOnly?: boolean;
|
||||
entityFqn?: string;
|
||||
entityFieldThreads?: EntityFieldThreads[];
|
||||
entityFieldTasks?: EntityFieldThreads[];
|
||||
onUpdate?: (columns: ModifiedTableColumn[]) => void;
|
||||
onThreadLinkSelect?: (value: string, threadType?: ThreadType) => void;
|
||||
onEntityFieldSelect?: (value: string) => void;
|
||||
}
|
@ -286,6 +286,33 @@ jest.mock('../../utils/TagsUtils', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('./EntityTable.constant', () => {
|
||||
return {
|
||||
TABLE_HEADERS: [
|
||||
{
|
||||
Header: 'Name',
|
||||
accessor: 'name',
|
||||
},
|
||||
{
|
||||
Header: 'Type',
|
||||
accessor: 'dataTypeDisplay',
|
||||
},
|
||||
{
|
||||
Header: 'Data Quality',
|
||||
accessor: 'columnTests',
|
||||
},
|
||||
{
|
||||
Header: 'Description',
|
||||
accessor: 'description',
|
||||
},
|
||||
{
|
||||
Header: 'Tags',
|
||||
accessor: 'tags',
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
describe('Test EntityTable Component', () => {
|
||||
it('Check if it has all child elements', async () => {
|
||||
const { container } = render(<EntityTable {...mockEntityTableProp} />, {
|
||||
|
@ -18,7 +18,7 @@ import {
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { Card } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { cloneDeep, isEmpty, isUndefined, lowerCase } from 'lodash';
|
||||
import { cloneDeep, isEmpty, lowerCase } from 'lodash';
|
||||
import {
|
||||
AggregationType,
|
||||
Bucket,
|
||||
@ -131,6 +131,9 @@ const Explore: React.FC<ExploreProps> = ({
|
||||
const [selectedAdvancedFields, setSelectedAdvancedField] = useState<
|
||||
Array<AdvanceField>
|
||||
>([]);
|
||||
const [isInitialFilterSet, setIsInitialFilterSet] = useState<boolean>(
|
||||
!isEmpty(initialFilter)
|
||||
);
|
||||
|
||||
const onAdvancedFieldSelect = (value: string) => {
|
||||
const flag = selectedAdvancedFields.some((field) => field.key === value);
|
||||
@ -505,7 +508,11 @@ const Explore: React.FC<ExploreProps> = ({
|
||||
|
||||
const getData = () => {
|
||||
if (!isMounting.current && previsouIndex === getCurrentIndex(tab)) {
|
||||
if (isInitialFilterSet) {
|
||||
forceSetAgg.current = isInitialFilterSet;
|
||||
} else {
|
||||
forceSetAgg.current = !isFilterSet;
|
||||
}
|
||||
fetchTableData();
|
||||
}
|
||||
};
|
||||
@ -565,7 +572,7 @@ const Explore: React.FC<ExploreProps> = ({
|
||||
if (searchResult) {
|
||||
updateSearchResults(searchResult.resSearchResults);
|
||||
setCount(searchResult.resSearchResults.data.hits.total.value);
|
||||
if (forceSetAgg.current || !isUndefined(initialFilter)) {
|
||||
if (forceSetAgg.current) {
|
||||
setAggregations(
|
||||
searchResult.resSearchResults.data.hits.hits.length > 0
|
||||
? getAggregationList(
|
||||
@ -573,6 +580,7 @@ const Explore: React.FC<ExploreProps> = ({
|
||||
)
|
||||
: getAggregationListFromQS(location.search)
|
||||
);
|
||||
setIsInitialFilterSet(false);
|
||||
} else {
|
||||
const aggServiceType = getAggregationList(
|
||||
searchResult.resAggServiceType.data.aggregations,
|
||||
|
@ -178,7 +178,7 @@ const FacetFilter: FunctionComponent<FacetProp> = ({
|
||||
className="sidebar-my-data-holder mt-2 mb-3"
|
||||
data-testid="show-deleted-cntnr">
|
||||
<div
|
||||
className="filter-group tw-justify-between tw-mb-2"
|
||||
className="filter-group tw-justify-between tw-mb-1"
|
||||
data-testid="filter-container-deleted">
|
||||
<div className="tw-flex">
|
||||
<div className="filters-title tw-w-36 tw-truncate custom-checkbox-label">
|
||||
|
Loading…
x
Reference in New Issue
Block a user