mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-12-28 07:58:31 +00:00
fix: activity feed card header alignment (#14580)
* fix feed-card header alignment issue by revert back the using 2 UserPopOverCard code and fix some css * fix role button on span tag issue * replace span and onClick with the Link * make the whole feed header text as paragraph * fix: Header in ActivityFeedCard component * fix a style issue * update unit test of feed-card-header --------- Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
This commit is contained in:
parent
2307807bbb
commit
156dc29687
@ -11,130 +11,108 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
findByTestId,
|
||||
findByText,
|
||||
queryByTestId,
|
||||
render,
|
||||
} from '@testing-library/react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { Thread, ThreadType } from '../../../../generated/entity/feed/thread';
|
||||
import FeedCardHeader from './FeedCardHeader';
|
||||
|
||||
const FQN = 'service.database.schema.table';
|
||||
const type = 'table';
|
||||
const expectedDisplayName = 'database.schema.table';
|
||||
|
||||
jest.mock('../../../../rest/userAPI', () => ({
|
||||
getUserByName: jest.fn().mockReturnValue({}),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../utils/CommonUtils', () => ({
|
||||
getPartialNameFromFQN: jest.fn().mockReturnValue('feedcard'),
|
||||
getNonDeletedTeams: jest.fn().mockReturnValue([]),
|
||||
getEntityName: jest.fn().mockReturnValue('entityname'),
|
||||
getPartialNameFromTableFQN: jest.fn().mockImplementation(() => {
|
||||
return expectedDisplayName;
|
||||
}),
|
||||
getEntityDetailLink: jest.fn().mockImplementation(() => expectedDisplayName),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../utils/TableUtils', () => ({
|
||||
getTierTags: jest.fn(),
|
||||
getTagsWithoutTier: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../common/ProfilePicture/ProfilePicture', () => {
|
||||
return jest.fn().mockReturnValue(<p>ProfilePicture</p>);
|
||||
});
|
||||
|
||||
const mockFeedHeaderProps = {
|
||||
createdBy: 'xyz',
|
||||
entityFQN: 'x.y.v.z',
|
||||
entityField: 'z',
|
||||
entityType: 'y',
|
||||
const mockProps1 = {
|
||||
createdBy: 'username',
|
||||
entityFQN: 'service.database.schema.table',
|
||||
entityField: 'entityField',
|
||||
entityType: 'table',
|
||||
isEntityFeed: true,
|
||||
timeStamp: 1647322547179,
|
||||
feedType: ThreadType.Conversation,
|
||||
task: {} as Thread,
|
||||
};
|
||||
|
||||
const mockProps2 = {
|
||||
...mockProps1,
|
||||
isEntityFeed: false,
|
||||
};
|
||||
|
||||
jest.mock('../../../../constants/constants', () => ({
|
||||
getUserPath: jest.fn().mockReturnValue('user-profile-path'),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../hooks/user-profile/useUserProfile', () => ({
|
||||
useUserProfile: jest.fn().mockReturnValue([]),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../utils/date-time/DateTimeUtils', () => ({
|
||||
formatDateTime: jest.fn().mockImplementation((date) => date),
|
||||
getRelativeTime: jest.fn().mockImplementation((date) => date),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../utils/EntityUtils', () => ({
|
||||
getEntityName: jest.fn().mockReturnValue('username'),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../utils/FeedUtils', () => ({
|
||||
entityDisplayName: jest.fn().mockReturnValue('database.schema.table'),
|
||||
getEntityFieldDisplay: jest
|
||||
.fn()
|
||||
.mockImplementation((entityField) => entityField),
|
||||
prepareFeedLink: jest.fn().mockReturnValue('entity-link'),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../utils/TasksUtils', () => ({
|
||||
getTaskDetailPath: jest.fn().mockReturnValue('task detail path'),
|
||||
}));
|
||||
|
||||
jest.mock('../../../common/PopOverCard/EntityPopOverCard', () =>
|
||||
jest.fn().mockImplementation(({ children }) => (
|
||||
<>
|
||||
EntityPopOverCard
|
||||
<div>{children}</div>
|
||||
</>
|
||||
))
|
||||
);
|
||||
|
||||
jest.mock('../../../common/PopOverCard/UserPopOverCard', () =>
|
||||
jest.fn().mockImplementation(({ children }) => (
|
||||
<>
|
||||
UserPopOverCard
|
||||
<div>{children}</div>
|
||||
</>
|
||||
))
|
||||
);
|
||||
|
||||
describe('Test FeedHeader Component', () => {
|
||||
it('Checks if the FeedHeader component has isEntityFeed as true', async () => {
|
||||
const { container } = render(<FeedCardHeader {...mockFeedHeaderProps} />, {
|
||||
it('should contain all necessary elements, when isEntityFeed true', () => {
|
||||
render(<FeedCardHeader {...mockProps1} />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
const createdBy = await findByText(container, /xyz/i);
|
||||
|
||||
const headerElement = await findByTestId(container, 'headerText');
|
||||
const entityFieldElement = await findByTestId(
|
||||
container,
|
||||
'headerText-entityField'
|
||||
);
|
||||
const entityTypeElement = queryByTestId(container, 'entityType');
|
||||
const entityLinkElement = queryByTestId(container, 'entitylink');
|
||||
const timeStampElement = await findByTestId(container, 'timestamp');
|
||||
expect(screen.getByText('UserPopOverCard')).toBeInTheDocument();
|
||||
expect(screen.getByText('username')).toBeInTheDocument();
|
||||
|
||||
expect(createdBy).toBeInTheDocument();
|
||||
expect(screen.getByTestId('headerText-entityField')).toBeInTheDocument();
|
||||
|
||||
expect(headerElement).toBeInTheDocument();
|
||||
expect(entityFieldElement).toBeInTheDocument();
|
||||
expect(entityTypeElement).not.toBeInTheDocument();
|
||||
expect(entityLinkElement).not.toBeInTheDocument();
|
||||
expect(timeStampElement).toBeInTheDocument();
|
||||
expect(timeStampElement).toHaveTextContent('1 year ago');
|
||||
expect(screen.getByTestId('timestamp')).toBeInTheDocument();
|
||||
|
||||
expect(screen.queryByTestId('table')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('entitylink')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Checks if the FeedHeader component has isEntityFeed as false', async () => {
|
||||
const { container } = render(
|
||||
<FeedCardHeader {...mockFeedHeaderProps} isEntityFeed={false} />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
const createdBy = await findByText(container, /xyz/i);
|
||||
it('should contain all necessary elements, when isEntityFeed false', () => {
|
||||
render(<FeedCardHeader {...mockProps2} />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
|
||||
const headerElement = await findByTestId(container, 'headerText');
|
||||
const entityFieldElement = queryByTestId(
|
||||
container,
|
||||
'headerText-entityField'
|
||||
);
|
||||
const entityTypeElement = await findByTestId(container, 'entityType');
|
||||
const entityLinkElement = await findByTestId(container, 'entitylink');
|
||||
const timeStampElement = await findByTestId(container, 'timestamp');
|
||||
expect(screen.getByText('UserPopOverCard')).toBeInTheDocument();
|
||||
expect(screen.getByText('username')).toBeInTheDocument();
|
||||
|
||||
expect(createdBy).toBeInTheDocument();
|
||||
expect(screen.getByText('table')).toBeInTheDocument();
|
||||
expect(screen.getByText('database.schema.table')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('entitylink')).toBeInTheDocument();
|
||||
|
||||
expect(headerElement).toBeInTheDocument();
|
||||
expect(entityFieldElement).not.toBeInTheDocument();
|
||||
expect(entityTypeElement).toBeInTheDocument();
|
||||
expect(entityLinkElement).toBeInTheDocument();
|
||||
expect(timeStampElement).toBeInTheDocument();
|
||||
expect(timeStampElement).toHaveTextContent('1 year ago');
|
||||
});
|
||||
expect(screen.getByTestId('timestamp')).toBeInTheDocument();
|
||||
|
||||
it('Should show link text as `database.schema.table` if entity type is table', async () => {
|
||||
const { container } = render(
|
||||
<FeedCardHeader
|
||||
{...mockFeedHeaderProps}
|
||||
entityFQN={FQN}
|
||||
entityType={type}
|
||||
isEntityFeed={false}
|
||||
/>,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const entityTypeElement = await findByTestId(container, 'entityType');
|
||||
const entityLinkElement = await findByTestId(container, 'entitylink');
|
||||
|
||||
expect(entityTypeElement).toBeInTheDocument();
|
||||
expect(entityLinkElement).toBeInTheDocument();
|
||||
|
||||
expect(entityTypeElement).toHaveTextContent(type);
|
||||
|
||||
expect(entityLinkElement).toHaveTextContent(expectedDisplayName);
|
||||
expect(
|
||||
screen.queryByTestId('headerText-entityField')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@ -17,11 +17,14 @@ import { isUndefined } from 'lodash';
|
||||
import React, { FC } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getUserPath } from '../../../../constants/constants';
|
||||
import { ThreadType } from '../../../../generated/entity/feed/thread';
|
||||
import { useUserProfile } from '../../../../hooks/user-profile/useUserProfile';
|
||||
import {
|
||||
formatDateTime,
|
||||
getRelativeTime,
|
||||
} from '../../../../utils/date-time/DateTimeUtils';
|
||||
import { getEntityName } from '../../../../utils/EntityUtils';
|
||||
import {
|
||||
entityDisplayName,
|
||||
getEntityFieldDisplay,
|
||||
@ -31,6 +34,7 @@ import { getTaskDetailPath } from '../../../../utils/TasksUtils';
|
||||
import EntityPopOverCard from '../../../common/PopOverCard/EntityPopOverCard';
|
||||
import UserPopOverCard from '../../../common/PopOverCard/UserPopOverCard';
|
||||
import { FeedHeaderProp } from '../ActivityFeedCard.interface';
|
||||
import './feed-card-header-v1.style.less';
|
||||
|
||||
const FeedCardHeader: FC<FeedHeaderProp> = ({
|
||||
className,
|
||||
@ -43,6 +47,11 @@ const FeedCardHeader: FC<FeedHeaderProp> = ({
|
||||
feedType,
|
||||
task,
|
||||
}) => {
|
||||
const [, , user] = useUserProfile({
|
||||
permission: true,
|
||||
name: createdBy ?? '',
|
||||
});
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { task: taskDetails } = task;
|
||||
@ -119,8 +128,12 @@ const FeedCardHeader: FC<FeedHeaderProp> = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={classNames('d-inline-block', className)}>
|
||||
<UserPopOverCard userName={createdBy}>{createdBy}</UserPopOverCard>
|
||||
<div className={classNames('d-inline-block feed-header', className)}>
|
||||
<UserPopOverCard userName={createdBy}>
|
||||
<Link className="thread-author m-r-xss" to={getUserPath(createdBy)}>
|
||||
{getEntityName(user)}
|
||||
</Link>
|
||||
</UserPopOverCard>
|
||||
|
||||
{feedType === ThreadType.Conversation && getFeedLinkElement}
|
||||
{feedType === ThreadType.Task && getTaskLinkElement}
|
||||
|
||||
@ -17,10 +17,13 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import EntityPopOverCard from '../../../../components/common/PopOverCard/EntityPopOverCard';
|
||||
import { getUserPath } from '../../../../constants/constants';
|
||||
import { useUserProfile } from '../../../../hooks/user-profile/useUserProfile';
|
||||
import {
|
||||
formatDateTime,
|
||||
getRelativeTime,
|
||||
} from '../../../../utils/date-time/DateTimeUtils';
|
||||
import { getEntityName } from '../../../../utils/EntityUtils';
|
||||
import {
|
||||
entityDisplayName,
|
||||
getEntityField,
|
||||
@ -49,6 +52,11 @@ const FeedCardHeaderV1 = ({
|
||||
className = '',
|
||||
isEntityFeed = false,
|
||||
}: FeedCardHeaderV1Props) => {
|
||||
const [, , user] = useUserProfile({
|
||||
permission: true,
|
||||
name: createdBy ?? '',
|
||||
});
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const entityType = getEntityType(entityLink) ?? '';
|
||||
@ -59,7 +67,7 @@ const FeedCardHeaderV1 = ({
|
||||
|
||||
const getFeedLinkElement = entityCheck && (
|
||||
<span className="font-normal" data-testid="headerText">
|
||||
<span className="m-x-xss">{t('label.posted-on-lowercase')}</span>
|
||||
<span className="m-r-xss">{t('label.posted-on-lowercase')}</span>
|
||||
{isEntityFeed ? (
|
||||
<span data-testid="headerText-entityField">
|
||||
{getEntityFieldDisplay(entityField)}
|
||||
@ -96,20 +104,25 @@ const FeedCardHeaderV1 = ({
|
||||
|
||||
return (
|
||||
<div className={classNames('feed-header', className)}>
|
||||
<UserPopOverCard
|
||||
showUserName
|
||||
className="thread-author"
|
||||
userName={createdBy}
|
||||
/>
|
||||
{getFeedLinkElement}
|
||||
<UserPopOverCard userName={createdBy} />
|
||||
|
||||
{timeStamp && (
|
||||
<Tooltip title={formatDateTime(timeStamp)}>
|
||||
<span className="feed-header-timestamp" data-testid="timestamp">
|
||||
{getRelativeTime(timeStamp)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
<p className="feed-header-content break-word">
|
||||
<UserPopOverCard userName={createdBy}>
|
||||
<Link className="thread-author m-r-xss" to={getUserPath(createdBy)}>
|
||||
{getEntityName(user)}
|
||||
</Link>
|
||||
</UserPopOverCard>
|
||||
|
||||
{getFeedLinkElement}
|
||||
|
||||
{timeStamp && (
|
||||
<Tooltip title={formatDateTime(timeStamp)}>
|
||||
<span className="feed-header-timestamp" data-testid="timestamp">
|
||||
{getRelativeTime(timeStamp)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
|
||||
.feed-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
|
||||
.thread-author {
|
||||
font-weight: 600;
|
||||
@ -31,9 +31,6 @@
|
||||
}
|
||||
.feed-header-content {
|
||||
line-height: 20px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,8 +20,5 @@
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
max-height: 90%;
|
||||
.feed-card-body {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user