mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-12-25 14:38:29 +00:00
feat: implemented export user functionality and refactor user tab (#11909)
* refactor user tab in team page * added manage button and import export option for user tab * integrated export for users from team * added unit test for user tab * addressing comments * addressing comments
This commit is contained in:
parent
66aa28a3ee
commit
5404440e41
@ -20,22 +20,18 @@ import {
|
||||
Row,
|
||||
Space,
|
||||
Switch,
|
||||
Table,
|
||||
Tabs,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { ItemType } from 'antd/lib/menu/hooks/useItems';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import { ReactComponent as IconEdit } from 'assets/svg/edit-new.svg';
|
||||
import { ReactComponent as ExportIcon } from 'assets/svg/ic-export.svg';
|
||||
import { ReactComponent as ImportIcon } from 'assets/svg/ic-import.svg';
|
||||
import { AxiosError } from 'axios';
|
||||
import classNames from 'classnames';
|
||||
import FilterTablePlaceHolder from 'components/common/error-with-placeholder/FilterTablePlaceHolder';
|
||||
import { ManageButtonItemLabel } from 'components/common/ManageButtonContentItem/ManageButtonContentItem.component';
|
||||
import TableDataCardV2 from 'components/common/table-data-card-v2/TableDataCardV2';
|
||||
import { UserSelectableList } from 'components/common/UserSelectableList/UserSelectableList.component';
|
||||
import { useEntityExportModalProvider } from 'components/Entity/EntityExportModalProvider/EntityExportModalProvider.component';
|
||||
import {
|
||||
GlobalSettingOptions,
|
||||
@ -51,7 +47,6 @@ import {
|
||||
isNil,
|
||||
isUndefined,
|
||||
lowerCase,
|
||||
orderBy,
|
||||
uniqueId,
|
||||
} from 'lodash';
|
||||
import { ExtraInfo } from 'Models';
|
||||
@ -69,7 +64,6 @@ import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { getSuggestions } from 'rest/miscAPI';
|
||||
import { exportTeam, restoreTeam } from 'rest/teamsAPI';
|
||||
import AppState from '../../AppState';
|
||||
import { ReactComponent as IconRemove } from '../../assets/svg/ic-remove.svg';
|
||||
import { ReactComponent as IconRestore } from '../../assets/svg/ic-restore.svg';
|
||||
import { ReactComponent as IconOpenLock } from '../../assets/svg/open-lock.svg';
|
||||
import { ReactComponent as IconShowPassword } from '../../assets/svg/show-password.svg';
|
||||
@ -77,7 +71,6 @@ import {
|
||||
getTeamAndUserDetailsPath,
|
||||
getUserPath,
|
||||
LIST_SIZE,
|
||||
PAGE_SIZE_MEDIUM,
|
||||
ROUTES,
|
||||
} from '../../constants/constants';
|
||||
import { ROLE_DOCS, TEAMS_DOCS } from '../../constants/docs.constants';
|
||||
@ -126,12 +119,12 @@ import {
|
||||
OperationPermission,
|
||||
ResourceEntity,
|
||||
} from '../PermissionProvider/PermissionProvider.interface';
|
||||
import { commonUserDetailColumns } from '../Users/Users.util';
|
||||
import ListEntities from './RolesAndPoliciesList';
|
||||
import { TeamsPageTab } from './team.interface';
|
||||
import { getTabs } from './TeamDetailsV1.utils';
|
||||
import TeamHierarchy from './TeamHierarchy';
|
||||
import './teams.less';
|
||||
import { UserTab } from './UserTab/UserTab.component';
|
||||
|
||||
const TeamDetailsV1 = ({
|
||||
assets,
|
||||
@ -316,42 +309,6 @@ const TeamDetailsV1 = ({
|
||||
[]
|
||||
);
|
||||
|
||||
const columns: ColumnsType<User> = useMemo(() => {
|
||||
return [
|
||||
...commonUserDetailColumns(),
|
||||
{
|
||||
title: t('label.action-plural'),
|
||||
dataIndex: 'actions',
|
||||
key: 'actions',
|
||||
width: 90,
|
||||
render: (_, record) => (
|
||||
<Space
|
||||
align="center"
|
||||
className="tw-w-full tw-justify-center remove-icon"
|
||||
size={8}>
|
||||
<Tooltip
|
||||
placement="bottomRight"
|
||||
title={
|
||||
entityPermissions.EditAll
|
||||
? t('label.remove')
|
||||
: t('message.no-permission-for-action')
|
||||
}>
|
||||
<Button
|
||||
data-testid="remove-user-btn"
|
||||
disabled={!entityPermissions.EditAll}
|
||||
icon={
|
||||
<IconRemove height={16} name={t('label.remove')} width={16} />
|
||||
}
|
||||
type="text"
|
||||
onClick={() => deleteUserHandler(record.id)}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
}, [deleteUserHandler]);
|
||||
|
||||
const ownerValue = useMemo(() => {
|
||||
switch (currentTeam.owner?.type) {
|
||||
case 'team':
|
||||
@ -842,114 +799,6 @@ const TeamDetailsV1 = ({
|
||||
]
|
||||
);
|
||||
|
||||
/**
|
||||
* Check for current team users and return the user cards
|
||||
* @returns - user cards
|
||||
*/
|
||||
const getUserCards = () => {
|
||||
const sortedUser = orderBy(currentTeamUsers || [], ['name'], 'asc');
|
||||
|
||||
return (
|
||||
<>
|
||||
{isEmpty(currentTeamUsers) &&
|
||||
!teamUsersSearchText &&
|
||||
isTeamMemberLoading <= 0 ? (
|
||||
fetchErrorPlaceHolder({
|
||||
type: ERROR_PLACEHOLDER_TYPE.ASSIGN,
|
||||
permission: entityPermissions.EditAll,
|
||||
heading: t('label.user'),
|
||||
button: (
|
||||
<UserSelectableList
|
||||
hasPermission
|
||||
selectedUsers={currentTeam.users ?? []}
|
||||
onUpdate={handleAddUser}>
|
||||
<Button
|
||||
ghost
|
||||
className="p-x-lg"
|
||||
data-testid="add-new-user"
|
||||
icon={<PlusOutlined />}
|
||||
title={
|
||||
entityPermissions.EditAll
|
||||
? t('label.add-new-entity', { entity: t('label.user') })
|
||||
: t('message.no-permission-for-action')
|
||||
}
|
||||
type="primary">
|
||||
{t('label.add')}
|
||||
</Button>
|
||||
</UserSelectableList>
|
||||
),
|
||||
})
|
||||
) : (
|
||||
<>
|
||||
<div className="d-flex tw-justify-between tw-items-center tw-mb-3">
|
||||
<div className="tw-w-4/12">
|
||||
<Searchbar
|
||||
removeMargin
|
||||
placeholder={t('label.search-for-type', {
|
||||
type: t('label.user-lowercase'),
|
||||
})}
|
||||
searchValue={teamUsersSearchText}
|
||||
typingInterval={500}
|
||||
onSearch={handleTeamUsersSearchAction}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{currentTeamUsers.length > 0 && isActionAllowed() && (
|
||||
<UserSelectableList
|
||||
hasPermission
|
||||
selectedUsers={currentTeam.users ?? []}
|
||||
onUpdate={handleAddUser}>
|
||||
<Button
|
||||
data-testid="add-new-user"
|
||||
disabled={!entityPermissions.EditAll}
|
||||
title={
|
||||
entityPermissions.EditAll
|
||||
? t('label.add-entity', { entity: t('label.user') })
|
||||
: t('message.no-permission-for-action')
|
||||
}
|
||||
type="primary">
|
||||
{t('label.add-entity', { entity: t('label.user') })}
|
||||
</Button>
|
||||
</UserSelectableList>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isTeamMemberLoading > 0 ? (
|
||||
<Loader />
|
||||
) : (
|
||||
<div>
|
||||
<Fragment>
|
||||
<Table
|
||||
bordered
|
||||
className="teams-list-table"
|
||||
columns={columns}
|
||||
dataSource={sortedUser}
|
||||
locale={{
|
||||
emptyText: <FilterTablePlaceHolder />,
|
||||
}}
|
||||
pagination={false}
|
||||
rowKey="name"
|
||||
size="small"
|
||||
/>
|
||||
{teamUserPagin.total > PAGE_SIZE_MEDIUM && (
|
||||
<NextPrevious
|
||||
currentPage={currentTeamUserPage}
|
||||
isNumberBased={Boolean(teamUsersSearchText)}
|
||||
pageSize={PAGE_SIZE_MEDIUM}
|
||||
paging={teamUserPagin}
|
||||
pagingHandler={teamUserPaginHandler}
|
||||
totalCount={teamUserPagin.total}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check for current team datasets and return the dataset cards
|
||||
* @returns - dataset cards
|
||||
@ -1315,7 +1164,21 @@ const TeamDetailsV1 = ({
|
||||
</Row>
|
||||
))}
|
||||
|
||||
{currentTab === TeamsPageTab.USERS && getUserCards()}
|
||||
{currentTab === TeamsPageTab.USERS && (
|
||||
<UserTab
|
||||
currentPage={currentTeamUserPage}
|
||||
currentTeam={currentTeam}
|
||||
isLoading={isTeamMemberLoading}
|
||||
paging={teamUserPagin}
|
||||
permission={entityPermissions}
|
||||
searchText={teamUsersSearchText}
|
||||
users={currentTeamUsers}
|
||||
onAddUser={handleAddUser}
|
||||
onChangePaging={teamUserPaginHandler}
|
||||
onRemoveUser={removeUserFromTeam}
|
||||
onSearchUsers={handleTeamUsersSearchAction}
|
||||
/>
|
||||
)}
|
||||
|
||||
{currentTab === TeamsPageTab.ASSETS && getAssetDetailCards()}
|
||||
|
||||
|
||||
@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright 2023 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 { PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Col, Row, Space, Table, Tooltip } from 'antd';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import { ReactComponent as ExportIcon } from 'assets/svg/ic-export.svg';
|
||||
import { ReactComponent as IconRemove } from 'assets/svg/ic-remove.svg';
|
||||
import ManageButton from 'components/common/entityPageInfo/ManageButton/ManageButton';
|
||||
import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
|
||||
import FilterTablePlaceHolder from 'components/common/error-with-placeholder/FilterTablePlaceHolder';
|
||||
import { ManageButtonItemLabel } from 'components/common/ManageButtonContentItem/ManageButtonContentItem.component';
|
||||
import NextPrevious from 'components/common/next-previous/NextPrevious';
|
||||
import Searchbar from 'components/common/searchbar/Searchbar';
|
||||
import { UserSelectableList } from 'components/common/UserSelectableList/UserSelectableList.component';
|
||||
import { useEntityExportModalProvider } from 'components/Entity/EntityExportModalProvider/EntityExportModalProvider.component';
|
||||
import Loader from 'components/Loader/Loader';
|
||||
import ConfirmationModal from 'components/Modals/ConfirmationModal/ConfirmationModal';
|
||||
import { commonUserDetailColumns } from 'components/Users/Users.util';
|
||||
import { PAGE_SIZE_MEDIUM } from 'constants/constants';
|
||||
import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum';
|
||||
import { User } from 'generated/entity/teams/user';
|
||||
import { EntityReference } from 'generated/entity/type';
|
||||
import { isEmpty, orderBy } from 'lodash';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { exportUserOfTeam } from 'rest/teamsAPI';
|
||||
import { getEntityName } from 'utils/EntityUtils';
|
||||
import { UserTabProps } from './UserTab.interface';
|
||||
|
||||
export const UserTab = ({
|
||||
users,
|
||||
searchText,
|
||||
isLoading,
|
||||
permission,
|
||||
currentTeam,
|
||||
onSearchUsers,
|
||||
onAddUser,
|
||||
paging,
|
||||
onChangePaging,
|
||||
currentPage,
|
||||
onRemoveUser,
|
||||
}: UserTabProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [deletingUser, setDeletingUser] = useState<EntityReference>();
|
||||
const { showModal } = useEntityExportModalProvider();
|
||||
const handleRemoveClick = (id: string) => {
|
||||
const user = currentTeam.users?.find((u) => u.id === id);
|
||||
setDeletingUser(user);
|
||||
};
|
||||
|
||||
const columns: ColumnsType<User> = useMemo(() => {
|
||||
return [
|
||||
...commonUserDetailColumns(),
|
||||
{
|
||||
title: t('label.action-plural'),
|
||||
dataIndex: 'actions',
|
||||
key: 'actions',
|
||||
width: 90,
|
||||
render: (_, record) => (
|
||||
<Space
|
||||
align="center"
|
||||
className="w-full justify-center remove-icon"
|
||||
size={8}>
|
||||
<Tooltip
|
||||
placement="bottomRight"
|
||||
title={
|
||||
permission.EditAll
|
||||
? t('label.remove')
|
||||
: t('message.no-permission-for-action')
|
||||
}>
|
||||
<Button
|
||||
data-testid="remove-user-btn"
|
||||
disabled={!permission.EditAll}
|
||||
icon={
|
||||
<IconRemove height={16} name={t('label.remove')} width={16} />
|
||||
}
|
||||
type="text"
|
||||
onClick={() => handleRemoveClick(record.id)}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
}, [handleRemoveClick, permission]);
|
||||
|
||||
const sortedUser = useMemo(() => orderBy(users, ['name'], 'asc'), [users]);
|
||||
|
||||
const handleUserExportClick = useCallback(async () => {
|
||||
if (currentTeam?.name) {
|
||||
showModal({
|
||||
name: currentTeam.name,
|
||||
onExport: exportUserOfTeam,
|
||||
});
|
||||
}
|
||||
}, [currentTeam, exportUserOfTeam]);
|
||||
|
||||
const IMPORT_EXPORT_MENU_ITEM = useMemo(
|
||||
() => [
|
||||
{
|
||||
label: (
|
||||
<ManageButtonItemLabel
|
||||
description={t('message.export-entity-help', {
|
||||
entity: t('label.user-lowercase'),
|
||||
})}
|
||||
icon={<ExportIcon width="18px" />}
|
||||
id="export"
|
||||
name={t('label.export')}
|
||||
/>
|
||||
),
|
||||
|
||||
onClick: handleUserExportClick,
|
||||
key: 'export-button',
|
||||
},
|
||||
],
|
||||
[handleUserExportClick]
|
||||
);
|
||||
|
||||
const handleRemoveUser = () => {
|
||||
if (deletingUser?.id) {
|
||||
onRemoveUser(deletingUser.id).then(() => {
|
||||
setDeletingUser(undefined);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (isEmpty(users) && !searchText && isLoading <= 0) {
|
||||
return (
|
||||
<ErrorPlaceHolder
|
||||
button={
|
||||
<UserSelectableList
|
||||
hasPermission
|
||||
selectedUsers={currentTeam.users ?? []}
|
||||
onUpdate={onAddUser}>
|
||||
<Button
|
||||
ghost
|
||||
className="p-x-lg"
|
||||
data-testid="add-new-user"
|
||||
icon={<PlusOutlined />}
|
||||
title={
|
||||
permission.EditAll
|
||||
? t('label.add-new-entity', { entity: t('label.user') })
|
||||
: t('message.no-permission-for-action')
|
||||
}
|
||||
type="primary">
|
||||
{t('label.add')}
|
||||
</Button>
|
||||
</UserSelectableList>
|
||||
}
|
||||
className="mt-0-important"
|
||||
heading={t('label.user')}
|
||||
permission={permission.EditAll}
|
||||
type={ERROR_PLACEHOLDER_TYPE.ASSIGN}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<Row justify="space-between">
|
||||
<Col span={8}>
|
||||
<Searchbar
|
||||
removeMargin
|
||||
placeholder={t('label.search-for-type', {
|
||||
type: t('label.user-lowercase'),
|
||||
})}
|
||||
searchValue={searchText}
|
||||
typingInterval={500}
|
||||
onSearch={onSearchUsers}
|
||||
/>
|
||||
</Col>
|
||||
<Col>
|
||||
<Space>
|
||||
{users.length > 0 && permission.EditAll && (
|
||||
<UserSelectableList
|
||||
hasPermission
|
||||
selectedUsers={currentTeam.users ?? []}
|
||||
onUpdate={onAddUser}>
|
||||
<Button data-testid="add-new-user" type="primary">
|
||||
{t('label.add-entity', { entity: t('label.user') })}
|
||||
</Button>
|
||||
</UserSelectableList>
|
||||
)}
|
||||
<ManageButton
|
||||
canDelete={false}
|
||||
entityName={currentTeam.name}
|
||||
extraDropdownContent={IMPORT_EXPORT_MENU_ITEM}
|
||||
/>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
|
||||
{isLoading > 0 ? (
|
||||
<Loader />
|
||||
) : (
|
||||
<Col span={24}>
|
||||
<Table
|
||||
bordered
|
||||
className="teams-list-table"
|
||||
columns={columns}
|
||||
dataSource={sortedUser}
|
||||
locale={{
|
||||
emptyText: <FilterTablePlaceHolder />,
|
||||
}}
|
||||
pagination={false}
|
||||
rowKey="name"
|
||||
size="small"
|
||||
/>
|
||||
{paging.total > PAGE_SIZE_MEDIUM && (
|
||||
<NextPrevious
|
||||
currentPage={currentPage}
|
||||
isNumberBased={Boolean(searchText)}
|
||||
pageSize={PAGE_SIZE_MEDIUM}
|
||||
paging={paging}
|
||||
pagingHandler={onChangePaging}
|
||||
totalCount={paging.total}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
)}
|
||||
|
||||
<ConfirmationModal
|
||||
bodyText={t('message.are-you-sure-want-to-text', {
|
||||
text: t('label.remove-entity', {
|
||||
entity: getEntityName(deletingUser),
|
||||
}),
|
||||
})}
|
||||
cancelText={t('label.cancel')}
|
||||
confirmText={t('label.confirm')}
|
||||
header={t('label.removing-user')}
|
||||
visible={Boolean(deletingUser)}
|
||||
onCancel={() => setDeletingUser(undefined)}
|
||||
onConfirm={handleRemoveUser}
|
||||
/>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2023 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 { OperationPermission } from 'components/PermissionProvider/PermissionProvider.interface';
|
||||
import { Team } from 'generated/entity/teams/team';
|
||||
import { User } from 'generated/entity/teams/user';
|
||||
import { EntityReference } from 'generated/type/entityReference';
|
||||
import { Paging } from 'generated/type/paging';
|
||||
|
||||
export interface UserTabProps {
|
||||
users: User[];
|
||||
searchText: string;
|
||||
isLoading: number;
|
||||
permission: OperationPermission;
|
||||
currentTeam: Team;
|
||||
onSearchUsers: (text: string) => void;
|
||||
onAddUser: (data: EntityReference[]) => void;
|
||||
paging: Paging;
|
||||
onChangePaging: (cursorValue: string | number, activePage?: number) => void;
|
||||
currentPage: number;
|
||||
onRemoveUser: (id: string) => Promise<void>;
|
||||
}
|
||||
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2023 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 {
|
||||
act,
|
||||
findByText,
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
} from '@testing-library/react';
|
||||
import { OperationPermission } from 'components/PermissionProvider/PermissionProvider.interface';
|
||||
import { pagingObject } from 'constants/constants';
|
||||
import { Team } from 'generated/entity/teams/team';
|
||||
import { User } from 'generated/entity/teams/user';
|
||||
import { MOCK_MARKETING_TEAM } from 'mocks/Teams.mock';
|
||||
import React from 'react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { UserTab } from './UserTab.component';
|
||||
import { UserTabProps } from './UserTab.interface';
|
||||
|
||||
const props: UserTabProps = {
|
||||
users: MOCK_MARKETING_TEAM.users as User[],
|
||||
searchText: '',
|
||||
isLoading: 0,
|
||||
permission: {
|
||||
EditAll: true,
|
||||
} as OperationPermission,
|
||||
currentTeam: MOCK_MARKETING_TEAM as Team,
|
||||
onSearchUsers: jest.fn(),
|
||||
onAddUser: jest.fn(),
|
||||
paging: pagingObject,
|
||||
onChangePaging: jest.fn(),
|
||||
currentPage: 1,
|
||||
onRemoveUser: jest.fn().mockResolvedValue('removed'),
|
||||
};
|
||||
jest.mock('components/common/error-with-placeholder/ErrorPlaceHolder', () => {
|
||||
return jest.fn().mockImplementation(() => <div>ErrorPlaceHolder</div>);
|
||||
});
|
||||
jest.mock('components/common/next-previous/NextPrevious', () => {
|
||||
return jest.fn().mockImplementation(() => <div>NextPrevious</div>);
|
||||
});
|
||||
jest.mock('components/common/searchbar/Searchbar', () => {
|
||||
return jest.fn().mockImplementation(() => <div>Searchbar</div>);
|
||||
});
|
||||
jest.mock('components/Loader/Loader', () => {
|
||||
return jest.fn().mockImplementation(() => <div>Loader</div>);
|
||||
});
|
||||
jest.mock('components/common/entityPageInfo/ManageButton/ManageButton', () => {
|
||||
return jest.fn().mockImplementation(() => <div>ManageButton</div>);
|
||||
});
|
||||
jest.mock('components/Modals/ConfirmationModal/ConfirmationModal', () => {
|
||||
return jest.fn().mockImplementation(({ onConfirm }) => (
|
||||
<div data-testid="confirmation-modal">
|
||||
<button onClick={onConfirm}>confirm</button>
|
||||
</div>
|
||||
));
|
||||
});
|
||||
jest.mock(
|
||||
'components/common/UserSelectableList/UserSelectableList.component',
|
||||
() => ({
|
||||
UserSelectableList: jest
|
||||
.fn()
|
||||
.mockImplementation(({ children }) => (
|
||||
<div data-testid="user-selectable-list">{children}</div>
|
||||
)),
|
||||
})
|
||||
);
|
||||
|
||||
describe('UserTab', () => {
|
||||
it('Component should render', async () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<UserTab {...props} />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
expect(await screen.findByRole('table')).toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByTestId('user-selectable-list')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByTestId('add-new-user')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Searchbar')).toBeInTheDocument();
|
||||
expect(await screen.findByText('ManageButton')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Error placeholder should visible if there is no data', async () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<UserTab {...props} users={[]} />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
expect(await screen.findByText('ErrorPlaceHolder')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Loader should visible if data is loading', async () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<UserTab {...props} isLoading={1} />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
expect(await screen.findByText('Loader')).toBeInTheDocument();
|
||||
expect(screen.queryByRole('table')).not.toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByTestId('user-selectable-list')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByTestId('add-new-user')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Searchbar')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Pagination should visible if total value is greater then 15', async () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<UserTab {...props} paging={{ total: 16 }} />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
expect(await screen.findByText('NextPrevious')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Remove user flow', async () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<UserTab {...props} />
|
||||
</BrowserRouter>
|
||||
);
|
||||
const removeBtn = await screen.findByTestId('remove-user-btn');
|
||||
|
||||
expect(removeBtn).toBeInTheDocument();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(removeBtn);
|
||||
});
|
||||
const confirmationModal = await screen.findByTestId('confirmation-modal');
|
||||
const confirmBtn = await findByText(confirmationModal, 'confirm');
|
||||
|
||||
expect(confirmationModal).toBeInTheDocument();
|
||||
expect(confirmBtn).toBeInTheDocument();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(confirmBtn);
|
||||
});
|
||||
|
||||
expect(props.onRemoveUser).toHaveBeenCalledWith(props.users[0].id);
|
||||
});
|
||||
});
|
||||
@ -202,6 +202,58 @@ export const MOCK_TABLE_DATA = [
|
||||
},
|
||||
];
|
||||
|
||||
export const MOCK_MARKETING_TEAM = {
|
||||
id: 'afa05b3f-bee4-4ead-8457-d82f0040faf8',
|
||||
teamType: 'Group',
|
||||
name: 'Marketing',
|
||||
fullyQualifiedName: 'Marketing',
|
||||
version: 0.1,
|
||||
updatedAt: 1686117247164,
|
||||
updatedBy: 'admin',
|
||||
href: 'test',
|
||||
parents: [
|
||||
{
|
||||
id: '7d82d6ca-9768-4a51-b4be-36c0f2add94c',
|
||||
type: 'team',
|
||||
name: 'Finance',
|
||||
fullyQualifiedName: 'Finance',
|
||||
deleted: false,
|
||||
href: 'test',
|
||||
},
|
||||
],
|
||||
users: [
|
||||
{
|
||||
id: '17c88b6f-8f21-40d3-afeb-30f5bdc2a537',
|
||||
type: 'user',
|
||||
name: 'aaron_warren5',
|
||||
fullyQualifiedName: 'aaron_warren5',
|
||||
displayName: 'Aaron Warren',
|
||||
deleted: false,
|
||||
href: 'test',
|
||||
email: 'aaron_warren5@gmail.com',
|
||||
},
|
||||
],
|
||||
childrenCount: 0,
|
||||
owns: [],
|
||||
isJoinable: true,
|
||||
deleted: false,
|
||||
defaultRoles: [],
|
||||
inheritedRoles: [
|
||||
{
|
||||
id: '829d9442-5e38-401b-8f32-8970c1290360',
|
||||
type: 'role',
|
||||
name: 'DataConsumer',
|
||||
fullyQualifiedName: 'DataConsumer',
|
||||
description:
|
||||
'Users with Data Consumer role use different data assets for their day to day work.',
|
||||
displayName: 'Data Consumer',
|
||||
deleted: false,
|
||||
href: 'test',
|
||||
},
|
||||
],
|
||||
policies: [],
|
||||
};
|
||||
|
||||
export const MOCK_CSV_TEAM_DATA = {
|
||||
rowData: [
|
||||
[
|
||||
|
||||
@ -438,7 +438,7 @@ const TeamsPage = () => {
|
||||
patchTeamDetail(selectedTeam.id, jsonPatch)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
fetchTeamByFqn(res.name);
|
||||
fetchTeamByFqn(res.name, false);
|
||||
} else {
|
||||
throw t('server.unexpected-response');
|
||||
}
|
||||
|
||||
@ -130,6 +130,14 @@ export const exportTeam = async (teamName: string) => {
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const exportUserOfTeam = async (team: string) => {
|
||||
const response = await APIClient.get<string>(`/users/export`, {
|
||||
params: { team },
|
||||
});
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const importTeam = async (
|
||||
teamName: string,
|
||||
data: string,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user