mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-26 18:06:03 +00:00
* activity feed ui improvements * fix mysql icon not showing in feed card * fix comments not showing in right panel card * remove unused code and added utils test * supported playwright test for feed replies * changes as per comments
This commit is contained in:
parent
6e99fe7bda
commit
e66aa5363b
@ -17,6 +17,7 @@ import {
|
||||
createNewPage,
|
||||
redirectToHomePage,
|
||||
toastNotification,
|
||||
visitUserProfilePage,
|
||||
} from '../../utils/common';
|
||||
import { clickOnLogo } from '../../utils/sidebar';
|
||||
import {
|
||||
@ -43,7 +44,6 @@ test.describe('Activity feed', () => {
|
||||
|
||||
test.beforeEach('Visit on landing page', async ({ page }) => {
|
||||
await redirectToHomePage(page);
|
||||
await entity.visitEntityPage(page);
|
||||
});
|
||||
|
||||
test.afterAll('Cleanup', async ({ browser }) => {
|
||||
@ -59,6 +59,7 @@ test.describe('Activity feed', () => {
|
||||
term: entity.entity.name,
|
||||
assignee: `${user.data.firstName}.${user.data.lastName}`,
|
||||
};
|
||||
await entity.visitEntityPage(page);
|
||||
|
||||
await page.getByTestId('request-description').click();
|
||||
|
||||
@ -126,4 +127,58 @@ test.describe('Activity feed', () => {
|
||||
|
||||
expect(closedTask).toContain('2 Closed');
|
||||
});
|
||||
|
||||
test('User should be able to reply on feeds in ActivityFeed', async ({
|
||||
page,
|
||||
}) => {
|
||||
await visitUserProfilePage(page);
|
||||
|
||||
const secondFeedConversation = page
|
||||
.locator('#center-container [data-testid="message-container"]')
|
||||
.nth(1);
|
||||
|
||||
await secondFeedConversation.locator('.feed-card-v2-sidebar').click();
|
||||
|
||||
await page.waitForSelector('#feed-panel', {
|
||||
state: 'visible',
|
||||
});
|
||||
|
||||
// Compare the text of the second feed in the center container with the right panel feed
|
||||
const secondFeedText = await secondFeedConversation
|
||||
.locator('[data-testid="headerText"]')
|
||||
.innerText();
|
||||
|
||||
const rightPanelFeedText = await page
|
||||
.locator(
|
||||
'.right-container [data-testid="message-container"] [data-testid="headerText"]'
|
||||
)
|
||||
.innerText();
|
||||
|
||||
expect(secondFeedText).toBe(rightPanelFeedText);
|
||||
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
await page.fill(
|
||||
'[data-testid="editor-wrapper"] .ql-editor',
|
||||
`Reply message ${i}`
|
||||
);
|
||||
const sendReply = page.waitForResponse('/api/v1/feed/*/posts');
|
||||
await page.getByTestId('send-button').click();
|
||||
await sendReply;
|
||||
}
|
||||
|
||||
// Compare after adding some feeds in the right panel
|
||||
const rightPanelFeedTextCurrent = await page
|
||||
.locator(
|
||||
'.right-container [data-testid="message-container"] [data-testid="headerText"]'
|
||||
)
|
||||
.innerText();
|
||||
|
||||
expect(secondFeedText).toBe(rightPanelFeedTextCurrent);
|
||||
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
await expect(
|
||||
page.locator('.right-container [data-testid="feed-replies"]')
|
||||
).toContainText(`Reply message ${i}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -147,3 +147,16 @@ export const clickOutside = async (page: Page) => {
|
||||
}); // with this action left menu bar is getting opened
|
||||
await page.mouse.move(1280, 0); // moving out side left menu bar to avoid random failure due to left menu bar
|
||||
};
|
||||
|
||||
export const visitUserProfilePage = async (page: Page) => {
|
||||
await page.getByTestId('dropdown-profile').click();
|
||||
await page.waitForSelector('.profile-dropdown', {
|
||||
state: 'visible',
|
||||
});
|
||||
const userResponse = page.waitForResponse(
|
||||
'/api/v1/users/name/*?fields=*&include=all'
|
||||
);
|
||||
await page.getByTestId('user-name').click({ force: true });
|
||||
await userResponse;
|
||||
await clickOutside(page);
|
||||
};
|
||||
|
@ -14,7 +14,6 @@
|
||||
import { Col, Row } from 'antd';
|
||||
import React, { useMemo } from 'react';
|
||||
import { EntityField } from '../../../../../constants/Feeds.constants';
|
||||
import { getTextDiff } from '../../../../../utils/EntityVersionUtils';
|
||||
import {
|
||||
getFeedChangeFieldLabel,
|
||||
getFieldOperationIcon,
|
||||
@ -25,13 +24,10 @@ import { DescriptionFeedProps } from './DescriptionFeed.interface';
|
||||
|
||||
function DescriptionFeed({ feed }: Readonly<DescriptionFeedProps>) {
|
||||
const { message, fieldOperation } = useMemo(() => {
|
||||
const diffText = getTextDiff(
|
||||
feed.feedInfo?.entitySpecificInfo?.previousDescription ?? '',
|
||||
feed.feedInfo?.entitySpecificInfo?.newDescription ?? ''
|
||||
);
|
||||
|
||||
return {
|
||||
message: diffText,
|
||||
message: (feed.feedInfo?.entitySpecificInfo?.diffMessage ?? '').split(
|
||||
':'
|
||||
)[1],
|
||||
fieldOperation: feed.fieldOperation,
|
||||
fieldChanged: getFeedChangeFieldLabel(
|
||||
feed.feedInfo?.fieldName as EntityField
|
||||
|
@ -44,7 +44,11 @@ const FeedPanelHeader: FC<FeedPanelHeaderProp> = ({
|
||||
const entityField = getEntityField(entityLink);
|
||||
|
||||
return (
|
||||
<header className={classNames('d-flex justify-between p-y-md', className)}>
|
||||
<header
|
||||
className={classNames(
|
||||
'd-flex justify-between items-center p-y-md',
|
||||
className
|
||||
)}>
|
||||
<p data-testid="header-title">
|
||||
<span data-testid="header-noun">
|
||||
{noun ? noun : getFeedPanelHeaderText(threadType)}{' '}
|
||||
|
@ -218,16 +218,6 @@ const ActivityFeedProvider = ({ children, user }: Props) => {
|
||||
|
||||
try {
|
||||
const res = await postFeedById(id, data);
|
||||
const { id: responseId, posts } = res;
|
||||
setEntityThread((pre) => {
|
||||
return pre.map((thread) => {
|
||||
if (thread.id === responseId) {
|
||||
return { ...res, posts: posts?.slice(-3) };
|
||||
} else {
|
||||
return thread;
|
||||
}
|
||||
});
|
||||
});
|
||||
setActiveThread(res);
|
||||
} catch (error) {
|
||||
showErrorToast(
|
||||
|
@ -69,12 +69,13 @@ const TagsViewer: FunctionComponent<TagsViewerProps> = ({
|
||||
const readMoreRenderElement = useMemo(
|
||||
() => (
|
||||
<div data-testid="read-more-element">
|
||||
{isOpen &&
|
||||
sortedTagsBySource.slice(sizeCap).map((tag) => (
|
||||
<p className="text-left" key={tag}>
|
||||
{getTagsElement(tag)}
|
||||
</p>
|
||||
))}
|
||||
{isOpen && (
|
||||
<div className="m-t-xs d-flex flex-wrap gap-2">
|
||||
{sortedTagsBySource
|
||||
.slice(sizeCap)
|
||||
.map((tag) => getTagsElement(tag))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasMoreElement && (
|
||||
<Button
|
||||
@ -94,18 +95,16 @@ const TagsViewer: FunctionComponent<TagsViewerProps> = ({
|
||||
);
|
||||
|
||||
const popoverRenderElement = useMemo(
|
||||
() => (
|
||||
<div className="m-t-xs" data-testid="popover-element">
|
||||
{sortedTagsBySource.slice(sizeCap).length > 0 && (
|
||||
() =>
|
||||
sortedTagsBySource.slice(sizeCap).length > 0 && (
|
||||
<div className="m-t-xs" data-testid="popover-element">
|
||||
<Popover
|
||||
content={
|
||||
<>
|
||||
{sortedTagsBySource.slice(sizeCap).map((tag) => (
|
||||
<p className="text-left" key={tag}>
|
||||
{getTagsElement(tag)}
|
||||
</p>
|
||||
))}
|
||||
</>
|
||||
<div className="d-flex flex-column flex-wrap gap-2">
|
||||
{sortedTagsBySource
|
||||
.slice(sizeCap)
|
||||
.map((tag) => getTagsElement(tag))}
|
||||
</div>
|
||||
}
|
||||
overlayClassName="tag-popover-container"
|
||||
placement="bottom"
|
||||
@ -116,9 +115,8 @@ const TagsViewer: FunctionComponent<TagsViewerProps> = ({
|
||||
sortedTagsBySource.length - (sizeCap ?? 0)
|
||||
} more`}</Tag>
|
||||
</Popover>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
</div>
|
||||
),
|
||||
|
||||
[sizeCap, sortedTagsBySource]
|
||||
);
|
||||
|
@ -1480,7 +1480,7 @@
|
||||
"feed-asset-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-custom-property-header": "updated Custom Properties on",
|
||||
"feed-entity-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for {{entity}}",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for",
|
||||
"feed-filter-all": "Feeds for all the data assets that you own and follow",
|
||||
"feed-filter-following": "Feeds for all the data assets that you follow",
|
||||
"feed-filter-owner": "Feeds for all the data assets that you own",
|
||||
|
@ -1565,7 +1565,7 @@
|
||||
"minute": "Minute",
|
||||
"modify-hierarchy-entity-description": "Modify the hierarchy by changing the Parent {{entity}}.",
|
||||
"most-active-users": "Displays the most active users on the platform based on Page Views.",
|
||||
"most-viewed-data-assets": "Displays the most viewed data assets.",
|
||||
"most-viewed-data-assets": "Displays the most viewed feed-field-action-entity-headerdata assets.",
|
||||
"mutually-exclusive-alert": "If you enable 'Mutually Exclusive' for a {{entity}}, users will be restricted to using only one {{child-entity}} to apply to a data asset. Once this option is activated, it cannot be deactivated.",
|
||||
"name-of-the-bucket-dbt-files-stored": "Name of the bucket where the dbt files are stored.",
|
||||
"new-conversation": "You are starting a new conversation",
|
||||
|
@ -1480,7 +1480,7 @@
|
||||
"feed-asset-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-custom-property-header": "updated Custom Properties on",
|
||||
"feed-entity-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for {{entity}}",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for",
|
||||
"feed-filter-all": "Feeds para todos los activos de datos que posee y sigue",
|
||||
"feed-filter-following": "Feeds para todos los activos de datos que sigue",
|
||||
"feed-filter-owner": "Feeds para todos los activos de datos que posee",
|
||||
|
@ -1480,7 +1480,7 @@
|
||||
"feed-asset-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-custom-property-header": "a mis à jour la propriété personnalisée le",
|
||||
"feed-entity-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for {{entity}}",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for",
|
||||
"feed-filter-all": "Flux de tous les actifs de données dont vous êtes propriétaire et que vous suivez",
|
||||
"feed-filter-following": "Flux de tous les actifs de données que vous suivez",
|
||||
"feed-filter-owner": "Flux de tous les actifs de données dont vous êtes propriétaire",
|
||||
|
@ -1480,7 +1480,7 @@
|
||||
"feed-asset-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-custom-property-header": "updated Custom Properties on",
|
||||
"feed-entity-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for {{entity}}",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for",
|
||||
"feed-filter-all": "פידים עבור כל הנכסים שאתה בעל ועוקב אחריהם",
|
||||
"feed-filter-following": "פידים עבור כל הנכסים שאתה עוקב אחריהם",
|
||||
"feed-filter-owner": "פידים עבור כל הנכסים שאתה בעל שלהם",
|
||||
|
@ -1480,7 +1480,7 @@
|
||||
"feed-asset-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-custom-property-header": "updated Custom Properties on",
|
||||
"feed-entity-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for {{entity}}",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for",
|
||||
"feed-filter-all": "Feeds for all the data assets that you own and follow",
|
||||
"feed-filter-following": "Feeds for all the data assets that you follow",
|
||||
"feed-filter-owner": "Feeds for all the data assets that you own",
|
||||
|
@ -1480,7 +1480,7 @@
|
||||
"feed-asset-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-custom-property-header": "updated Custom Properties on",
|
||||
"feed-entity-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for {{entity}}",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0>",
|
||||
"feed-filter-all": "Feeds voor alle data-assets waar je eigenaar van bent en volgt",
|
||||
"feed-filter-following": "Feeds voor alle data-assets die je volgt",
|
||||
"feed-filter-owner": "Feeds voor alle data-assets waar je eigenaar van bent",
|
||||
|
@ -1480,7 +1480,7 @@
|
||||
"feed-asset-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-custom-property-header": "updated Custom Properties on",
|
||||
"feed-entity-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for {{entity}}",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for",
|
||||
"feed-filter-all": "Feeds para todos os ativos de dados que você possui e segue",
|
||||
"feed-filter-following": "Feeds para todos os ativos de dados que você segue",
|
||||
"feed-filter-owner": "Feeds para todos os ativos de dados que você possui",
|
||||
|
@ -1480,7 +1480,7 @@
|
||||
"feed-asset-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-custom-property-header": "updated Custom Properties on",
|
||||
"feed-entity-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for {{entity}}",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for",
|
||||
"feed-filter-all": "Feeds for all the data assets that you own and follow",
|
||||
"feed-filter-following": "Feeds for all the data assets that you follow",
|
||||
"feed-filter-owner": "Feeds for all the data assets that you own",
|
||||
|
@ -1480,7 +1480,7 @@
|
||||
"feed-asset-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-custom-property-header": "updated Custom Properties on",
|
||||
"feed-entity-action-header": "{{action}} <0>data asset</0>",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for {{entity}}",
|
||||
"feed-field-action-entity-header": "{{action}} <0>{{field}}</0> for",
|
||||
"feed-filter-all": "Feeds for all the data assets that you own and follow",
|
||||
"feed-filter-following": "Feeds for all the data assets that you follow",
|
||||
"feed-filter-owner": "Feeds for all the data assets that you own",
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
getEntityFQN,
|
||||
getEntityType,
|
||||
getFeedHeaderTextFromCardStyle,
|
||||
getFieldOperationIcon,
|
||||
suggestions,
|
||||
} from './FeedUtils';
|
||||
|
||||
@ -274,3 +275,27 @@ describe('getFeedHeaderTextFromCardStyle', () => {
|
||||
expect(stringResult).toContain('label.updated-lowercase');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFieldOperationIcon', () => {
|
||||
it('should not return icon in case of operation updated', () => {
|
||||
const result = getFieldOperationIcon(FieldOperation.Updated);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return icon in case of operation added', () => {
|
||||
const result = getFieldOperationIcon(FieldOperation.Added);
|
||||
|
||||
const stringResult = JSON.stringify(result);
|
||||
|
||||
expect(stringResult).toContain(FieldOperation.Added);
|
||||
});
|
||||
|
||||
it('should return icon in case of operation deleted', () => {
|
||||
const result = getFieldOperationIcon(FieldOperation.Deleted);
|
||||
|
||||
const stringResult = JSON.stringify(result);
|
||||
|
||||
expect(stringResult).toContain(FieldOperation.Deleted);
|
||||
});
|
||||
});
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
import { RightOutlined } from '@ant-design/icons';
|
||||
import Icon from '@ant-design/icons/lib/components/Icon';
|
||||
import { Typography } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
@ -640,21 +641,24 @@ export const getFeedChangeFieldLabel = (fieldName?: EntityField) => {
|
||||
};
|
||||
|
||||
export const getFieldOperationIcon = (fieldOperation?: FieldOperation) => {
|
||||
let Icon = UpdatedIcon;
|
||||
let icon;
|
||||
|
||||
switch (fieldOperation) {
|
||||
case FieldOperation.Added:
|
||||
Icon = AddIcon;
|
||||
icon = AddIcon;
|
||||
|
||||
break;
|
||||
case FieldOperation.Updated:
|
||||
case FieldOperation.Deleted:
|
||||
Icon = UpdatedIcon;
|
||||
icon = UpdatedIcon;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return <Icon height={16} width={16} />;
|
||||
return (
|
||||
icon && (
|
||||
<Icon component={icon} height={16} name={fieldOperation} width={16} />
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export const getTestCaseNameListForResult = (
|
||||
|
@ -202,6 +202,7 @@ export const getEntityIcon = (
|
||||
const entityIconMapping: Record<string, SvgComponent> = {
|
||||
[SearchIndex.DATABASE]: DatabaseIcon,
|
||||
[EntityType.DATABASE]: DatabaseIcon,
|
||||
[EntityType.DATABASE_SERVICE]: DatabaseIcon,
|
||||
[SearchIndex.DATABASE_SCHEMA]: SchemaIcon,
|
||||
[EntityType.DATABASE_SCHEMA]: SchemaIcon,
|
||||
[SearchIndex.TOPIC]: TopicIcon,
|
||||
@ -252,16 +253,18 @@ export const getEntityIcon = (
|
||||
[EntityType.ROLE]: RoleIcon,
|
||||
[EntityType.POLICY]: PolicyIcon,
|
||||
[EntityType.EVENT_SUBSCRIPTION]: AlertIcon,
|
||||
[EntityType.USER]: UserIcon,
|
||||
[SearchIndex.USER]: UserIcon,
|
||||
[EntityType.INGESTION_PIPELINE]: PipelineIcon,
|
||||
[SearchIndex.INGESTION_PIPELINE]: PipelineIcon,
|
||||
[EntityType.ALERT]: AlertIcon,
|
||||
['tagCategory']: ClassificationIcon,
|
||||
['ingestionPipeline']: PipelineIcon,
|
||||
['alert']: AlertIcon,
|
||||
['announcement']: AnnouncementIcon,
|
||||
['conversation']: ConversationIcon,
|
||||
['task']: TaskIcon,
|
||||
['dataQuality']: DataQualityIcon,
|
||||
['services']: ServicesIcon,
|
||||
['automator']: AutomatorBotIcon,
|
||||
['user']: UserIcon,
|
||||
['notification']: NotificationIcon,
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user