Fix owner dropdown for manage tab (#4820)

This commit is contained in:
darth-coder00 2022-05-11 03:48:54 +05:30 committed by GitHub
parent 14e09bd1cc
commit 6583981b5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 182 additions and 79 deletions

View File

@ -261,7 +261,9 @@ const DashboardDetails = ({
: dashboardDetails.tags;
const updatedDashboardDetails = {
...dashboardDetails,
owner: newOwner ? newOwner : dashboardDetails.owner,
owner: newOwner
? { ...dashboardDetails.owner, ...newOwner }
: dashboardDetails.owner,
tags: tierTag,
};
@ -678,7 +680,7 @@ const DashboardDetails = ({
<ManageTabComponent
allowDelete
currentTier={tier?.tagFQN}
currentUser={owner?.id}
currentUser={owner}
deletEntityMessage={getDeleteEntityMessage()}
entityId={dashboardDetails.id}
entityName={dashboardDetails.name}

View File

@ -745,7 +745,7 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
<ManageTab
allowDelete
currentTier={tier?.tagFQN}
currentUser={owner?.id}
currentUser={owner}
entityId={tableDetails.id}
entityName={tableDetails.name}
entityType={EntityType.TABLE}

View File

@ -393,7 +393,7 @@ const GlossaryDetails = ({
hideTier
isRecursiveDelete
afterDeleteAction={afterDeleteAction}
currentUser={glossary?.owner?.id}
currentUser={glossary?.owner}
entityId={glossary.id}
entityName={glossary?.name}
entityType={EntityType.GLOSSARY}

View File

@ -13,15 +13,15 @@
import { AxiosError, AxiosResponse } from 'axios';
import classNames from 'classnames';
import { isUndefined } from 'lodash';
import { isEqual, isUndefined } from 'lodash';
import { observer } from 'mobx-react';
import { TableDetail } from 'Models';
import React, { Fragment, FunctionComponent, useEffect, useState } from 'react';
import appState from '../../AppState';
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
import { getCategory } from '../../axiosAPIs/tagAPI';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { Operation } from '../../generated/entity/policies/accessControl/rule';
import { EntityReference } from '../../generated/type/entityReference';
import { useAuth } from '../../hooks/authHooks';
import jsonData from '../../jsons/en';
import { getOwnerList } from '../../utils/ManageUtils';
@ -36,7 +36,7 @@ import { ManageProps, Status } from './ManageTab.interface';
const ManageTab: FunctionComponent<ManageProps> = ({
currentTier = '',
currentUser = '',
currentUser,
hideTier = false,
hideOwner = false,
onSave,
@ -67,10 +67,6 @@ const ManageTab: FunctionComponent<ManageProps> = ({
const [owner, setOwner] = useState(currentUser);
const [isLoadingTierData, setIsLoadingTierData] = useState<boolean>(false);
const getOwnerById = (): string => {
return listOwners.find((item) => item.value === owner)?.name || '';
};
const setInitialOwnerLoadingState = () => {
setStatusOwner('initial');
};
@ -79,24 +75,20 @@ const ManageTab: FunctionComponent<ManageProps> = ({
setStatusTier('initial');
};
const prepareOwner = (updatedOwner: string) => {
return updatedOwner !== currentUser
? {
id: updatedOwner,
type: listOwners.find((item) => item.value === updatedOwner)?.type as
| 'user'
| 'team',
}
: undefined;
const prepareOwner = (updatedOwner?: EntityReference) => {
return !isEqual(updatedOwner, currentUser) ? updatedOwner : undefined;
};
const prepareTier = (updatedTier: string) => {
return updatedTier !== currentTier ? updatedTier : undefined;
};
const handleOwnerSave = (updatedOwner: string, updatedTier: string) => {
const handleOwnerSave = (
updatedOwner: EntityReference,
updatedTier: string
) => {
setStatusOwner('waiting');
const newOwner: TableDetail['owner'] = prepareOwner(updatedOwner);
const newOwner = prepareOwner(updatedOwner);
if (hideTier) {
if (newOwner || !isUndefined(teamJoinable)) {
onSave?.(newOwner, '', Boolean(teamJoinable)).catch(() => {
@ -115,7 +107,7 @@ const ManageTab: FunctionComponent<ManageProps> = ({
const handleTierSave = (updatedTier: string) => {
setStatusTier('waiting');
const newOwner: TableDetail['owner'] = prepareOwner(currentUser);
const newOwner = prepareOwner(currentUser);
if (hideTier) {
if (newOwner || !isUndefined(teamJoinable)) {
onSave?.(newOwner, '', Boolean(teamJoinable)).catch(() => {
@ -139,8 +131,9 @@ const ManageTab: FunctionComponent<ManageProps> = ({
if (value) {
const newOwner = listOwners.find((item) => item.value === value);
if (newOwner) {
setOwner(newOwner.value);
handleOwnerSave(newOwner.value, activeTier);
const { value: id, type } = newOwner;
setOwner({ id, type });
handleOwnerSave({ id, type }, activeTier);
}
}
setListVisible(false);
@ -223,7 +216,6 @@ const ManageTab: FunctionComponent<ManageProps> = ({
);
};
const ownerName = getOwnerById();
const getTierData = () => {
setIsLoadingTierData(true);
getCategory('Tier')
@ -322,8 +314,8 @@ const ManageTab: FunctionComponent<ManageProps> = ({
isJoinableActionAllowed={isJoinableActionAllowed()}
listOwners={listOwners}
listVisible={listVisible}
owner={owner}
ownerName={ownerName}
owner={owner || ({} as EntityReference)}
ownerName={currentUser?.displayName || currentUser?.name || ''}
statusOwner={statusOwner}
teamJoinable={teamJoinable}
/>

View File

@ -12,18 +12,19 @@
*/
import { TableDetail } from 'Models';
import { EntityReference } from '../../generated/type/entityReference';
export interface ManageProps {
currentTier?: string;
currentUser?: string;
currentUser?: EntityReference;
manageSectionType?: string;
hideTier?: boolean;
hideOwner?: boolean;
isJoinable?: boolean;
allowSoftDelete?: boolean;
onSave?: (
owner: TableDetail['owner'],
tier: TableDetail['tier'],
owner?: EntityReference,
tier?: TableDetail['tier'],
isJoinable?: boolean
) => Promise<void>;
handleIsJoinable?: (bool: boolean) => void;

View File

@ -270,7 +270,9 @@ const PipelineDetails = ({
: pipelineDetails.tags;
const updatedPipelineDetails = {
...pipelineDetails,
owner: newOwner ? newOwner : pipelineDetails.owner,
owner: newOwner
? { ...pipelineDetails.owner, ...newOwner }
: pipelineDetails.owner,
tags: tierTag,
};
@ -596,7 +598,7 @@ const PipelineDetails = ({
<ManageTabComponent
allowDelete
currentTier={tier?.tagFQN}
currentUser={owner?.id}
currentUser={owner}
entityId={pipelineDetails.id}
entityName={pipelineDetails.name}
entityType={EntityType.PIPELINE}

View File

@ -15,7 +15,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classNames from 'classnames';
import { compare } from 'fast-json-patch';
import { cloneDeep, isUndefined, orderBy } from 'lodash';
import { ExtraInfo, TableDetail } from 'Models';
import { ExtraInfo } from 'Models';
import React, { Fragment, useEffect, useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
import AppState from '../../AppState';
@ -32,6 +32,7 @@ import {
EntityReference as UserTeams,
User,
} from '../../generated/entity/teams/user';
import { EntityReference } from '../../generated/type/entityReference';
import { useAuth } from '../../hooks/authHooks';
import { TeamDetailsProp } from '../../interface/teamsAndUsers.interface';
import UserCard from '../../pages/teams/UserCard';
@ -236,11 +237,11 @@ const TeamDetails = ({
}
};
const handleManageSave = (owner: TableDetail['owner']) => {
const handleManageSave = (owner?: EntityReference) => {
if (currentTeam) {
const updatedData: Team = {
...currentTeam,
owner: owner,
owner: !isUndefined(owner) ? owner : currentTeam.owner,
};
return updateTeamHandler(updatedData);
@ -626,7 +627,7 @@ const TeamDetails = ({
hideTier
afterDeleteAction={afterDeleteAction}
allowTeamOwner={false}
currentUser={currentTeam.owner?.id}
currentUser={currentTeam.owner}
entityId={currentTeam.id}
entityName={currentTeam.displayName || currentTeam.name}
entityType="team"

View File

@ -465,7 +465,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
<ManageTabComponent
allowDelete
currentTier={tier?.tagFQN}
currentUser={owner?.id}
currentUser={owner}
entityId={topicDetails.id}
entityName={topicDetails.name}
entityType={EntityType.TOPIC}

View File

@ -16,6 +16,7 @@ import classNames from 'classnames';
import { isUndefined } from 'lodash';
import React, { Fragment } from 'react';
import { Operation } from '../../../generated/entity/policies/policy';
import { EntityReference } from '../../../generated/type/entityReference';
import { useAuth } from '../../../hooks/authHooks';
import { getTitleCase } from '../../../utils/EntityUtils';
import { Button } from '../../buttons/Button/Button';
@ -35,7 +36,7 @@ interface OwnerWidgetProps {
ownerName: string;
entityType?: string;
statusOwner: Status;
owner: string;
owner?: EntityReference;
listOwners: {
name: string;
value: string;
@ -153,11 +154,12 @@ const OwnerWidget = ({
{listVisible && (
<DropDownList
horzPosRight
showEmptyList
showSearchBar
dropDownList={listOwners}
groupType="tab"
listGroups={getOwnerGroup()}
value={owner}
value={owner?.id || ''}
onSelect={handleOwnerSelection}
/>
)}

View File

@ -15,6 +15,7 @@ import {
fireEvent,
getAllByTestId,
getByTestId,
queryByTestId,
render,
} from '@testing-library/react';
import React from 'react';
@ -82,4 +83,51 @@ describe('Test DropDownList Component', () => {
expect(MockOnSelect).toBeCalledTimes(1);
});
it('On search, show no result placeholder', () => {
const { container } = render(
<DropDownList
showEmptyList
showSearchBar
dropDownList={dropDownList}
listGroups={listGroups}
value=""
onSelect={MockOnSelect}
/>
);
const searchbar = getByTestId(container, 'searchInputText');
fireEvent.change(searchbar, {
target: {
value: 'test X',
},
});
const noMatchElement = getByTestId(container, 'empty-list');
expect(noMatchElement).toBeInTheDocument();
});
it('On search, do not show dropdown if no result matched', () => {
const { container } = render(
<DropDownList
showSearchBar
dropDownList={dropDownList}
listGroups={listGroups}
value=""
onSelect={MockOnSelect}
/>
);
const searchbar = getByTestId(container, 'searchInputText');
fireEvent.change(searchbar, {
target: {
value: 'test X',
},
});
const dropdownElement = queryByTestId(container, 'dropdown-list');
expect(dropdownElement).not.toBeInTheDocument();
});
});

View File

@ -27,6 +27,7 @@ const DropDownList: FunctionComponent<DropDownListProp> = ({
horzPosRight,
searchString = '',
showSearchBar = false,
showEmptyList = false,
value,
onSelect,
groupType = 'label',
@ -62,6 +63,18 @@ const DropDownList: FunctionComponent<DropDownListProp> = ({
setSearchText(text || '');
};
const getEmptyTextElement = (): JSX.Element => {
return (
<div
className="tw-text-grey-muted tw-px-4 tw-py-2"
data-testid="empty-list">
<p className={widthClass}>
{searchText ? 'No match found' : 'No data available'}
</p>
</div>
);
};
const getSearchedListByGroup = (
groupName?: string
): Array<DropDownListItem> => {
@ -95,6 +108,77 @@ const DropDownList: FunctionComponent<DropDownListProp> = ({
);
};
const getListElements = (): JSX.Element => {
const results = getSearchedListByGroup();
if (!results.length && showEmptyList && !listGroups.length) {
return getEmptyTextElement();
}
return (
<>
{results.map((item: DropDownListItem, index: number) =>
getDropDownElement(item, index)
)}
</>
);
};
const getListElementsByLabels = (groupName: string) => {
const results = getSearchedListByGroup(groupName);
const groupLabel = (
<span className="tw-flex tw-my-1 tw-text-grey-muted">
<hr className="tw-mt-2 tw-w-full" />
<span className="tw-text-xs tw-px-0.5">{groupName}</span>{' '}
<hr className="tw-mt-2 tw-w-full" />
</span>
);
if (!results.length && showEmptyList) {
return (
<>
{groupLabel}
{getEmptyTextElement()}
</>
);
}
return (
<>
{results.length > 0 && groupLabel}
{results.map((item: DropDownListItem, index: number) =>
getDropDownElement(item, index)
)}
</>
);
};
const getListElementsByTab = (groupTab: string) => {
const results = getSearchedListByGroup(groupTab);
if (!results.length && showEmptyList) {
return getEmptyTextElement();
}
return (
<>
{results.map((item: DropDownListItem, index: number) =>
getDropDownElement(item, index)
)}
</>
);
};
const getListElementsByGroup = () => {
if (groupType === 'label') {
return listGroups.map((grp, index) => {
return <div key={index}>{getListElementsByLabels(grp)}</div>;
});
} else {
return getListElementsByTab(listGroups[activeTab - 1]);
}
};
useEffect(() => {
setSearchText(searchString);
}, [searchString]);
@ -138,7 +222,7 @@ const DropDownList: FunctionComponent<DropDownListProp> = ({
return (
<>
{searchedList.length > 0 && (
{(searchedList.length > 0 || showEmptyList) && (
<>
<button
className="tw-z-10 tw-fixed tw-inset-0 tw-h-full tw-w-full tw-bg-black tw-opacity-0"
@ -201,38 +285,8 @@ const DropDownList: FunctionComponent<DropDownListProp> = ({
<div
className="tw-py-1 tw-max-h-60 tw-overflow-y-auto"
role="none">
{getSearchedListByGroup().map(
(item: DropDownListItem, index: number) =>
getDropDownElement(item, index)
)}
{groupType === 'label' ? (
listGroups.map((grp, index) => {
return (
<div key={index}>
{getSearchedListByGroup(grp).length > 0 && (
<span className="tw-flex tw-my-1 tw-text-grey-muted">
<hr className="tw-mt-2 tw-w-full " />
<span className="tw-text-xs tw-px-0.5">
{grp}
</span>{' '}
<hr className="tw-mt-2 tw-w-full" />
</span>
)}
{getSearchedListByGroup(grp).map(
(item: DropDownListItem, index: number) =>
getDropDownElement(item, index)
)}
</div>
);
})
) : (
<>
{getSearchedListByGroup(listGroups[activeTab - 1]).map(
(item: DropDownListItem, index: number) =>
getDropDownElement(item, index)
)}
</>
)}
{getListElements()}
{getListElementsByGroup()}
</div>
</>
)}

View File

@ -46,6 +46,7 @@ export type DropDownListProp = {
disabledItems?: Array<string>;
hiddenItems?: Array<string>;
showSearchBar?: boolean;
showEmptyList?: boolean;
value?: string;
onSelect?: (
event: React.MouseEvent<HTMLElement, MouseEvent>,

View File

@ -360,7 +360,7 @@ const DatabaseSchemaPage: FunctionComponent = () => {
const handleUpdateOwner = (owner: DatabaseSchema['owner']) => {
const updatedData = {
...databaseSchema,
owner,
owner: { ...databaseSchema?.owner, ...owner },
};
return new Promise<void>((_, reject) => {
@ -691,7 +691,7 @@ const DatabaseSchemaPage: FunctionComponent = () => {
allowDelete
hideTier
isRecursiveDelete
currentUser={databaseSchema?.owner?.id}
currentUser={databaseSchema?.owner}
deletEntityMessage={getDeleteEntityMessage()}
entityId={databaseSchema?.id}
entityName={databaseSchema?.name}

View File

@ -395,7 +395,7 @@ const DatabaseDetails: FunctionComponent = () => {
const handleUpdateOwner = (owner: Database['owner']) => {
const updatedData = {
...database,
owner,
owner: { ...database?.owner, ...owner },
};
return new Promise<void>((_, reject) => {
@ -807,7 +807,7 @@ const DatabaseDetails: FunctionComponent = () => {
allowDelete
hideTier
isRecursiveDelete
currentUser={database?.owner?.id}
currentUser={database?.owner}
deletEntityMessage={getDeleteEntityMessage()}
entityId={database?.id}
entityName={database?.name}

View File

@ -1011,7 +1011,7 @@ const ServicePage: FunctionComponent = () => {
allowDelete
hideTier
isRecursiveDelete
currentUser={serviceDetails?.owner?.id}
currentUser={serviceDetails?.owner}
deletEntityMessage={getDeleteEntityMessage()}
entityId={serviceDetails?.id}
entityName={serviceDetails?.name}

View File

@ -867,7 +867,7 @@ const TeamsPage = () => {
<ManageTabComponent
hideTier
allowTeamOwner={false}
currentUser={currentTeam?.owner?.id}
currentUser={currentTeam?.owner}
hasEditAccess={isOwner()}
isJoinable={currentTeam?.isJoinable}
onSave={handleUpdateTeam}