mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-02 19:48:17 +00:00
Fix owner dropdown for manage tab (#4820)
This commit is contained in:
parent
14e09bd1cc
commit
6583981b5c
@ -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}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -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>,
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -867,7 +867,7 @@ const TeamsPage = () => {
|
||||
<ManageTabComponent
|
||||
hideTier
|
||||
allowTeamOwner={false}
|
||||
currentUser={currentTeam?.owner?.id}
|
||||
currentUser={currentTeam?.owner}
|
||||
hasEditAccess={isOwner()}
|
||||
isJoinable={currentTeam?.isJoinable}
|
||||
onSave={handleUpdateTeam}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user