mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-30 18:17:53 +00:00
fix(ui): user page scroll issue on tabs and pagination (#12424)
* fix user page scroll on tabs and pagination too * changes as per comments * fix unit test
This commit is contained in:
parent
fc29eba285
commit
00934279c6
@ -139,8 +139,7 @@ const mockProp = {
|
|||||||
updateThreadHandler: jest.fn(),
|
updateThreadHandler: jest.fn(),
|
||||||
setFeedFilter: jest.fn(),
|
setFeedFilter: jest.fn(),
|
||||||
threadType: 'Task' as ThreadType.Task,
|
threadType: 'Task' as ThreadType.Task,
|
||||||
onFollowingEntityPaginate: jest.fn(),
|
handlePaginate: jest.fn(),
|
||||||
onOwnedEntityPaginate: jest.fn(),
|
|
||||||
onSwitchChange: jest.fn(),
|
onSwitchChange: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,17 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Card, Image, Input, Select, Space, Tabs, Typography } from 'antd';
|
import {
|
||||||
|
Card,
|
||||||
|
Col,
|
||||||
|
Image,
|
||||||
|
Input,
|
||||||
|
Row,
|
||||||
|
Select,
|
||||||
|
Space,
|
||||||
|
Tabs,
|
||||||
|
Typography,
|
||||||
|
} from 'antd';
|
||||||
import { ReactComponent as EditIcon } from 'assets/svg/edit-new.svg';
|
import { ReactComponent as EditIcon } from 'assets/svg/edit-new.svg';
|
||||||
import { ReactComponent as IconTeamsGrey } from 'assets/svg/teams-grey.svg';
|
import { ReactComponent as IconTeamsGrey } from 'assets/svg/teams-grey.svg';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
@ -80,8 +90,7 @@ const Users = ({
|
|||||||
isLoggedinUser,
|
isLoggedinUser,
|
||||||
isAuthDisabled,
|
isAuthDisabled,
|
||||||
username,
|
username,
|
||||||
onFollowingEntityPaginate,
|
handlePaginate,
|
||||||
onOwnedEntityPaginate,
|
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { tab = UserPageTabs.ACTIVITY } = useParams<{ tab: UserPageTabs }>();
|
const { tab = UserPageTabs.ACTIVITY } = useParams<{ tab: UserPageTabs }>();
|
||||||
const [displayName, setDisplayName] = useState(userData.displayName);
|
const [displayName, setDisplayName] = useState(userData.displayName);
|
||||||
@ -692,48 +701,42 @@ const Users = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayoutV1
|
<Row className="user-page-layout" wrap={false}>
|
||||||
className="user-page-layout"
|
<Col className="user-layout-scroll" flex="auto">
|
||||||
pageTitle={t('label.user')}
|
{entityData.data.length ? (
|
||||||
rightPanel={
|
<SearchedData
|
||||||
showSummaryPanel &&
|
data={entityData.data ?? []}
|
||||||
entityDetails && (
|
handleSummaryPanelDisplay={handleSummaryPanelDisplay}
|
||||||
|
isFilterSelected={false}
|
||||||
|
isSummaryPanelVisible={showSummaryPanel}
|
||||||
|
selectedEntityId={entityDetails?.id || ''}
|
||||||
|
totalValue={entityData.total ?? 0}
|
||||||
|
onPaginationChange={handlePaginate}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ErrorPlaceHolder className="m-0">
|
||||||
|
<Typography.Paragraph>
|
||||||
|
{tab === UserPageTabs.MY_DATA
|
||||||
|
? t('server.you-have-not-action-anything-yet', {
|
||||||
|
action: t('label.owned-lowercase'),
|
||||||
|
})
|
||||||
|
: t('server.you-have-not-action-anything-yet', {
|
||||||
|
action: t('label.followed-lowercase'),
|
||||||
|
})}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
</ErrorPlaceHolder>
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
{showSummaryPanel && entityDetails && (
|
||||||
|
<Col className="user-page-layout-right-panel " flex="400px">
|
||||||
<EntitySummaryPanel
|
<EntitySummaryPanel
|
||||||
entityDetails={{ details: entityDetails }}
|
entityDetails={{ details: entityDetails }}
|
||||||
handleClosePanel={handleClosePanel}
|
handleClosePanel={handleClosePanel}
|
||||||
/>
|
/>
|
||||||
)
|
</Col>
|
||||||
}
|
|
||||||
rightPanelWidth={400}>
|
|
||||||
{entityData.data.length ? (
|
|
||||||
<SearchedData
|
|
||||||
currentPage={entityData.currPage}
|
|
||||||
data={entityData.data ?? []}
|
|
||||||
handleSummaryPanelDisplay={handleSummaryPanelDisplay}
|
|
||||||
isFilterSelected={false}
|
|
||||||
isSummaryPanelVisible={showSummaryPanel}
|
|
||||||
selectedEntityId={entityDetails?.id || ''}
|
|
||||||
totalValue={entityData.total ?? 0}
|
|
||||||
onPaginationChange={
|
|
||||||
tab === UserPageTabs.MY_DATA
|
|
||||||
? onOwnedEntityPaginate
|
|
||||||
: onFollowingEntityPaginate
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ErrorPlaceHolder className="m-0">
|
|
||||||
<Typography.Paragraph>
|
|
||||||
{tab === UserPageTabs.MY_DATA
|
|
||||||
? t('server.you-have-not-action-anything-yet', {
|
|
||||||
action: t('label.owned-lowercase'),
|
|
||||||
})
|
|
||||||
: t('server.you-have-not-action-anything-yet', {
|
|
||||||
action: t('label.followed-lowercase'),
|
|
||||||
})}
|
|
||||||
</Typography.Paragraph>
|
|
||||||
</ErrorPlaceHolder>
|
|
||||||
)}
|
)}
|
||||||
</PageLayoutV1>
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case UserPageTabs.ACTIVITY:
|
case UserPageTabs.ACTIVITY:
|
||||||
|
|||||||
@ -19,21 +19,18 @@ export interface Props {
|
|||||||
followingEntities: {
|
followingEntities: {
|
||||||
data: SearchedDataProps['data'];
|
data: SearchedDataProps['data'];
|
||||||
total: number;
|
total: number;
|
||||||
currPage: number;
|
|
||||||
};
|
};
|
||||||
ownedEntities: {
|
ownedEntities: {
|
||||||
data: SearchedDataProps['data'];
|
data: SearchedDataProps['data'];
|
||||||
total: number;
|
total: number;
|
||||||
currPage: number;
|
|
||||||
};
|
};
|
||||||
username: string;
|
username: string;
|
||||||
isUserEntitiesLoading: boolean;
|
isUserEntitiesLoading: boolean;
|
||||||
isAdminUser: boolean;
|
isAdminUser: boolean;
|
||||||
isLoggedinUser: boolean;
|
isLoggedinUser: boolean;
|
||||||
isAuthDisabled: boolean;
|
isAuthDisabled: boolean;
|
||||||
|
handlePaginate: (page: string | number) => void;
|
||||||
updateUserDetails: (data: Partial<User>) => Promise<void>;
|
updateUserDetails: (data: Partial<User>) => Promise<void>;
|
||||||
onFollowingEntityPaginate: (page: string | number) => void;
|
|
||||||
onOwnedEntityPaginate: (page: string | number) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum UserPageTabs {
|
export enum UserPageTabs {
|
||||||
|
|||||||
@ -29,8 +29,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.user-page-layout {
|
.user-page-layout {
|
||||||
height: @users-page-tabs-height;
|
.user-layout-scroll {
|
||||||
.page-layout-rightpanel {
|
height: @users-page-tabs-height;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-page-layout-right-panel {
|
||||||
padding-right: 0 !important;
|
padding-right: 0 !important;
|
||||||
background-color: @white;
|
background-color: @white;
|
||||||
border: 1px solid @border-color;
|
border: 1px solid @border-color;
|
||||||
|
|||||||
@ -91,5 +91,4 @@ export interface SearchedDataProps {
|
|||||||
entityType: string
|
entityType: string
|
||||||
) => void;
|
) => void;
|
||||||
filter?: Qs.ParsedQs;
|
filter?: Qs.ParsedQs;
|
||||||
currentPage?: number;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,7 +48,6 @@ const SearchedData: React.FC<SearchedDataProps> = ({
|
|||||||
selectedEntityId,
|
selectedEntityId,
|
||||||
handleSummaryPanelDisplay,
|
handleSummaryPanelDisplay,
|
||||||
filter,
|
filter,
|
||||||
currentPage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const searchResultCards = useMemo(() => {
|
const searchResultCards = useMemo(() => {
|
||||||
return data.map(({ _source: table, highlight }, index) => {
|
return data.map(({ _source: table, highlight }, index) => {
|
||||||
@ -159,11 +158,7 @@ const SearchedData: React.FC<SearchedDataProps> = ({
|
|||||||
<Pagination
|
<Pagination
|
||||||
hideOnSinglePage
|
hideOnSinglePage
|
||||||
className="text-center"
|
className="text-center"
|
||||||
current={
|
current={isNumber(Number(page)) ? Number(page) : 1}
|
||||||
isNumber(Number(page ?? currentPage))
|
|
||||||
? Number(page ?? currentPage)
|
|
||||||
: 1
|
|
||||||
}
|
|
||||||
pageSize={
|
pageSize={
|
||||||
size && isNumber(Number(size))
|
size && isNumber(Number(size))
|
||||||
? Number(size)
|
? Number(size)
|
||||||
|
|||||||
@ -18,10 +18,16 @@ import Users from 'components/Users/Users.component';
|
|||||||
import { compare } from 'fast-json-patch';
|
import { compare } from 'fast-json-patch';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { AssetsDataType } from 'Models';
|
import Qs from 'qs';
|
||||||
import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
import React, {
|
||||||
|
Dispatch,
|
||||||
|
SetStateAction,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { searchData } from 'rest/miscAPI';
|
import { searchData } from 'rest/miscAPI';
|
||||||
import { getUserByName, updateUserDetail } from 'rest/userAPI';
|
import { getUserByName, updateUserDetail } from 'rest/userAPI';
|
||||||
import AppState from '../../AppState';
|
import AppState from '../../AppState';
|
||||||
@ -32,8 +38,10 @@ import { User } from '../../generated/entity/teams/user';
|
|||||||
import { useAuth } from '../../hooks/authHooks';
|
import { useAuth } from '../../hooks/authHooks';
|
||||||
import { SearchEntityHits } from '../../utils/APIUtils';
|
import { SearchEntityHits } from '../../utils/APIUtils';
|
||||||
import { showErrorToast } from '../../utils/ToastUtils';
|
import { showErrorToast } from '../../utils/ToastUtils';
|
||||||
|
import { UserAssetsDataType } from './UserPage.interface';
|
||||||
|
|
||||||
const UserPage = () => {
|
const UserPage = () => {
|
||||||
|
const history = useHistory();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { username, tab = UserProfileTab.ACTIVITY } =
|
const { username, tab = UserProfileTab.ACTIVITY } =
|
||||||
useParams<{ [key: string]: string }>();
|
useParams<{ [key: string]: string }>();
|
||||||
@ -46,17 +54,26 @@ const UserPage = () => {
|
|||||||
const [isUserEntitiesLoading, setIsUserEntitiesLoading] =
|
const [isUserEntitiesLoading, setIsUserEntitiesLoading] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
|
|
||||||
const [followingEntities, setFollowingEntities] = useState<AssetsDataType>({
|
const [followingEntities, setFollowingEntities] =
|
||||||
|
useState<UserAssetsDataType>({
|
||||||
|
data: [],
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
const [ownedEntities, setOwnedEntities] = useState<UserAssetsDataType>({
|
||||||
data: [],
|
data: [],
|
||||||
total: 0,
|
total: 0,
|
||||||
currPage: 1,
|
|
||||||
});
|
|
||||||
const [ownedEntities, setOwnedEntities] = useState<AssetsDataType>({
|
|
||||||
data: [],
|
|
||||||
total: 0,
|
|
||||||
currPage: 1,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { page = 1 } = useMemo(
|
||||||
|
() =>
|
||||||
|
Qs.parse(
|
||||||
|
location.search.startsWith('?')
|
||||||
|
? location.search.substr(1)
|
||||||
|
: location.search
|
||||||
|
),
|
||||||
|
[location.search]
|
||||||
|
);
|
||||||
|
|
||||||
const fetchUserData = () => {
|
const fetchUserData = () => {
|
||||||
setUserData({} as User);
|
setUserData({} as User);
|
||||||
getUserByName(username, 'profile,roles,teams')
|
getUserByName(username, 'profile,roles,teams')
|
||||||
@ -95,16 +112,14 @@ const UserPage = () => {
|
|||||||
|
|
||||||
const fetchEntities = async (
|
const fetchEntities = async (
|
||||||
fetchOwnedEntities = false,
|
fetchOwnedEntities = false,
|
||||||
handleEntity: Dispatch<SetStateAction<AssetsDataType>>
|
handleEntity: Dispatch<SetStateAction<UserAssetsDataType>>
|
||||||
) => {
|
) => {
|
||||||
const entity = fetchOwnedEntities ? ownedEntities : followingEntities;
|
|
||||||
|
|
||||||
if (userData.id) {
|
if (userData.id) {
|
||||||
setIsUserEntitiesLoading(true);
|
setIsUserEntitiesLoading(true);
|
||||||
try {
|
try {
|
||||||
const response = await searchData(
|
const response = await searchData(
|
||||||
'',
|
'',
|
||||||
entity.currPage,
|
Number(page),
|
||||||
PAGE_SIZE,
|
PAGE_SIZE,
|
||||||
getQueryFilters(fetchOwnedEntities),
|
getQueryFilters(fetchOwnedEntities),
|
||||||
'',
|
'',
|
||||||
@ -118,14 +133,12 @@ const UserPage = () => {
|
|||||||
handleEntity({
|
handleEntity({
|
||||||
data: hits,
|
data: hits,
|
||||||
total,
|
total,
|
||||||
currPage: entity.currPage,
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const total = 0;
|
const total = 0;
|
||||||
handleEntity({
|
handleEntity({
|
||||||
data: [],
|
data: [],
|
||||||
total,
|
total,
|
||||||
currPage: entity.currPage,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -141,12 +154,10 @@ const UserPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFollowingEntityPaginate = (page: string | number) => {
|
const handleEntityPaginate = (page: string | number) => {
|
||||||
setFollowingEntities((pre) => ({ ...pre, currPage: page as number }));
|
history.push({
|
||||||
};
|
search: Qs.stringify({ page }),
|
||||||
|
});
|
||||||
const handleOwnedEntityPaginate = (page: string | number) => {
|
|
||||||
setOwnedEntities((pre) => ({ ...pre, currPage: page as number }));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ErrorPlaceholder = () => {
|
const ErrorPlaceholder = () => {
|
||||||
@ -189,6 +200,7 @@ const UserPage = () => {
|
|||||||
return (
|
return (
|
||||||
<Users
|
<Users
|
||||||
followingEntities={followingEntities}
|
followingEntities={followingEntities}
|
||||||
|
handlePaginate={handleEntityPaginate}
|
||||||
isAdminUser={Boolean(isAdminUser)}
|
isAdminUser={Boolean(isAdminUser)}
|
||||||
isAuthDisabled={Boolean(isAuthDisabled)}
|
isAuthDisabled={Boolean(isAuthDisabled)}
|
||||||
isLoggedinUser={isLoggedinUser(username)}
|
isLoggedinUser={isLoggedinUser(username)}
|
||||||
@ -197,8 +209,6 @@ const UserPage = () => {
|
|||||||
updateUserDetails={updateUserDetails}
|
updateUserDetails={updateUserDetails}
|
||||||
userData={userData}
|
userData={userData}
|
||||||
username={username}
|
username={username}
|
||||||
onFollowingEntityPaginate={handleFollowingEntityPaginate}
|
|
||||||
onOwnedEntityPaginate={handleOwnedEntityPaginate}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -214,13 +224,13 @@ const UserPage = () => {
|
|||||||
if (tab === UserProfileTab.FOLLOWING) {
|
if (tab === UserProfileTab.FOLLOWING) {
|
||||||
fetchEntities(false, setFollowingEntities);
|
fetchEntities(false, setFollowingEntities);
|
||||||
}
|
}
|
||||||
}, [followingEntities.currPage, tab, userData]);
|
}, [page, tab, userData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (tab === UserProfileTab.MY_DATA) {
|
if (tab === UserProfileTab.MY_DATA) {
|
||||||
fetchEntities(true, setOwnedEntities);
|
fetchEntities(true, setOwnedEntities);
|
||||||
}
|
}
|
||||||
}, [ownedEntities.currPage, tab, userData]);
|
}, [page, tab, userData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurrentLoggedInUser(AppState.getCurrentUserDetails());
|
setCurrentLoggedInUser(AppState.getCurrentUserDetails());
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* 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 { SearchEntityHits } from 'utils/APIUtils';
|
||||||
|
|
||||||
|
export interface UserAssetsDataType {
|
||||||
|
data: SearchEntityHits;
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
@ -104,6 +104,7 @@ jest.mock('components/authentication/auth-provider/AuthProvider', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
jest.mock('react-router-dom', () => ({
|
jest.mock('react-router-dom', () => ({
|
||||||
|
useHistory: jest.fn(),
|
||||||
useParams: jest.fn().mockImplementation(() => ({ username: 'xyz' })),
|
useParams: jest.fn().mockImplementation(() => ({ username: 'xyz' })),
|
||||||
useLocation: jest.fn().mockImplementation(() => new URLSearchParams()),
|
useLocation: jest.fn().mockImplementation(() => new URLSearchParams()),
|
||||||
}));
|
}));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user