Fix #6635 UI : Announcement improvements (#6647)

* Fix #6635 UI : Announcement improvements

* Add unit test

* Fix unit test

* Fix announcement banner is not showing up!
This commit is contained in:
Sachin Chaurasiya 2022-08-09 11:29:04 +05:30 committed by GitHub
parent 77ad1b1c87
commit a9c010b03d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 82 additions and 14 deletions

View File

@ -172,13 +172,16 @@ export const updateTask: Function = (
}; };
export const getActiveAnnouncement = async (entityLink: string) => { export const getActiveAnnouncement = async (entityLink: string) => {
const response = await APIClient.get<Thread[]>('/feed', { const response = await APIClient.get<{ data: Thread[]; paging: Paging }>(
'/feed',
{
params: { params: {
entityLink, entityLink,
type: ThreadType.Announcement, type: ThreadType.Announcement,
activeAnnouncement: true, activeAnnouncement: true,
}, },
}); }
);
return response.data; return response.data;
}; };

View File

@ -12,6 +12,7 @@
*/ */
import { HTMLAttributes } from 'react'; import { HTMLAttributes } from 'react';
import { ThreadType } from '../../../generated/api/feed/createThread';
import { Thread } from '../../../generated/entity/feed/thread'; import { Thread } from '../../../generated/entity/feed/thread';
import { ThreadUpdatedFunc } from '../../../interface/feed.interface'; import { ThreadUpdatedFunc } from '../../../interface/feed.interface';
import { ConfirmState } from '../ActivityFeedCard/ActivityFeedCard.interface'; import { ConfirmState } from '../ActivityFeedCard/ActivityFeedCard.interface';
@ -29,7 +30,9 @@ export interface FeedPanelHeaderProp
extends HTMLAttributes<HTMLHeadingElement>, extends HTMLAttributes<HTMLHeadingElement>,
Pick<ActivityFeedPanelProp, 'onCancel'> { Pick<ActivityFeedPanelProp, 'onCancel'> {
entityField: string; entityField: string;
entityFQN?: string;
noun?: string; noun?: string;
threadType?: ThreadType;
onShowNewConversation?: (v: boolean) => void; onShowNewConversation?: (v: boolean) => void;
} }
export interface FeedPanelOverlayProp export interface FeedPanelOverlayProp

View File

@ -57,6 +57,7 @@ const mockFeedPanelProp = {
jest.mock('../../../utils/FeedUtils', () => ({ jest.mock('../../../utils/FeedUtils', () => ({
getEntityField: jest.fn(), getEntityField: jest.fn(),
getEntityFQN: jest.fn(),
})); }));
jest.mock('../ActivityFeedEditor/ActivityFeedEditor', () => { jest.mock('../ActivityFeedEditor/ActivityFeedEditor', () => {

View File

@ -18,7 +18,7 @@ import { getFeedById } from '../../../axiosAPIs/feedsAPI';
import { confirmStateInitialValue } from '../../../constants/feed.constants'; import { confirmStateInitialValue } from '../../../constants/feed.constants';
import { Thread } from '../../../generated/entity/feed/thread'; import { Thread } from '../../../generated/entity/feed/thread';
import jsonData from '../../../jsons/en'; import jsonData from '../../../jsons/en';
import { getEntityField } from '../../../utils/FeedUtils'; import { getEntityField, getEntityFQN } from '../../../utils/FeedUtils';
import { showErrorToast } from '../../../utils/ToastUtils'; import { showErrorToast } from '../../../utils/ToastUtils';
import { ConfirmState } from '../ActivityFeedCard/ActivityFeedCard.interface'; import { ConfirmState } from '../ActivityFeedCard/ActivityFeedCard.interface';
import ActivityFeedEditor from '../ActivityFeedEditor/ActivityFeedEditor'; import ActivityFeedEditor from '../ActivityFeedEditor/ActivityFeedEditor';
@ -40,6 +40,7 @@ const ActivityFeedPanel: FC<ActivityFeedPanelProp> = ({
const [threadData, setThreadData] = useState<Thread>(selectedThread); const [threadData, setThreadData] = useState<Thread>(selectedThread);
const [isLoading, setIsLoading] = useState<boolean>(false); const [isLoading, setIsLoading] = useState<boolean>(false);
const entityField = getEntityField(selectedThread.about); const entityField = getEntityField(selectedThread.about);
const entityFQN = getEntityFQN(selectedThread.about);
const [confirmationState, setConfirmationState] = useState<ConfirmState>( const [confirmationState, setConfirmationState] = useState<ConfirmState>(
confirmStateInitialValue confirmStateInitialValue
@ -88,7 +89,9 @@ const ActivityFeedPanel: FC<ActivityFeedPanelProp> = ({
id="feed-panel"> id="feed-panel">
<FeedPanelHeader <FeedPanelHeader
className="tw-px-4 tw-shadow-sm" className="tw-px-4 tw-shadow-sm"
entityFQN={entityFQN}
entityField={entityField as string} entityField={entityField as string}
threadType={selectedThread.type}
onCancel={onCancel} onCancel={onCancel}
/> />

View File

@ -14,6 +14,7 @@
import { findByTestId, queryByTestId, render } from '@testing-library/react'; import { findByTestId, queryByTestId, render } from '@testing-library/react';
import React from 'react'; import React from 'react';
import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom';
import { ThreadType } from '../../../generated/entity/feed/thread';
import FeedPanelHeader from './FeedPanelHeader'; import FeedPanelHeader from './FeedPanelHeader';
const mockFeedPanelHeaderProp = { const mockFeedPanelHeaderProp = {
@ -85,4 +86,40 @@ describe('Test FeedPanelHeader Component', () => {
// noun is undefined so default noun should be present in text content // noun is undefined so default noun should be present in text content
expect(noun).toHaveTextContent('Conversation on'); expect(noun).toHaveTextContent('Conversation on');
}); });
it('Should render entityFQN if entityField is empty', async () => {
const { container } = render(
<FeedPanelHeader
{...mockFeedPanelHeaderProp}
entityFQN="x.y.z"
entityField=""
/>,
{
wrapper: MemoryRouter,
}
);
const entityAttribute = await findByTestId(container, 'entity-attribute');
expect(entityAttribute).toHaveTextContent(/x.y.z/i);
});
it('Should render noun according to the threadtype', async () => {
const { container } = render(
<FeedPanelHeader
{...mockFeedPanelHeaderProp}
entityFQN="x.y.z"
entityField=""
noun={undefined}
threadType={ThreadType.Announcement}
/>,
{
wrapper: MemoryRouter,
}
);
const noun = await findByTestId(container, 'header-noun');
expect(noun).toHaveTextContent(/Announcement on/i);
});
}); });

View File

@ -14,7 +14,10 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classNames from 'classnames'; import classNames from 'classnames';
import React, { FC } from 'react'; import React, { FC } from 'react';
import { getEntityFieldDisplay } from '../../../utils/FeedUtils'; import {
getEntityFieldDisplay,
getFeedPanelHeaderText,
} from '../../../utils/FeedUtils';
import { Button } from '../../buttons/Button/Button'; import { Button } from '../../buttons/Button/Button';
import PopOver from '../../common/popover/PopOver'; import PopOver from '../../common/popover/PopOver';
import { FeedPanelHeaderProp } from './ActivityFeedPanel.interface'; import { FeedPanelHeaderProp } from './ActivityFeedPanel.interface';
@ -24,16 +27,18 @@ const FeedPanelHeader: FC<FeedPanelHeaderProp> = ({
className, className,
noun, noun,
onShowNewConversation, onShowNewConversation,
threadType,
entityFQN = '',
}) => { }) => {
return ( return (
<header className={className}> <header className={className}>
<div className="tw-flex tw-justify-between tw-py-3"> <div className="tw-flex tw-justify-between tw-py-3">
<p data-testid="header-title"> <p data-testid="header-title">
<span data-testid="header-noun"> <span data-testid="header-noun">
{noun ? noun : 'Conversation'} on{' '} {noun ? noun : getFeedPanelHeaderText(threadType)} on{' '}
</span> </span>
<span className="tw-heading"> <span className="tw-heading" data-testid="entity-attribute">
{getEntityFieldDisplay(entityField)} {entityField ? getEntityFieldDisplay(entityField) : entityFQN}
</span> </span>
</p> </p>
<div className="tw-flex"> <div className="tw-flex">

View File

@ -15,6 +15,7 @@ import { CloseOutlined } from '@ant-design/icons';
import { Button, Drawer, Space, Typography } from 'antd'; import { Button, Drawer, Space, Typography } from 'antd';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import { Operation } from 'fast-json-patch'; import { Operation } from 'fast-json-patch';
import { uniqueId } from 'lodash';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import React, { FC, useMemo, useState } from 'react'; import React, { FC, useMemo, useState } from 'react';
import AppState from '../../../../AppState'; import AppState from '../../../../AppState';
@ -123,6 +124,7 @@ const AnnouncementDrawer: FC<Props> = ({
className="tw-p-0" className="tw-p-0"
createThread={createThread} createThread={createThread}
deletePostHandler={deletePostHandler} deletePostHandler={deletePostHandler}
key={uniqueId()}
postFeedHandler={postFeedHandler} postFeedHandler={postFeedHandler}
showHeader={false} showHeader={false}
threadLink={getEntityFeedLink(entityType, entityFQN)} threadLink={getEntityFeedLink(entityType, entityFQN)}

View File

@ -377,8 +377,8 @@ const EntityPageInfo = ({
getEntityFeedLink(entityType, entityFqn) getEntityFeedLink(entityType, entityFqn)
); );
if (!isEmpty(announcements)) { if (!isEmpty(announcements.data)) {
setActiveAnnouncement(announcements[0]); setActiveAnnouncement(announcements.data[0]);
} }
} catch (error) { } catch (error) {
showErrorToast(error as AxiosError); showErrorToast(error as AxiosError);

View File

@ -506,3 +506,17 @@ export const entityDisplayName = (entityType: string, entityFQN: string) => {
export const MarkdownToHTMLConverter = new Showdown.Converter({ export const MarkdownToHTMLConverter = new Showdown.Converter({
strikethrough: true, strikethrough: true,
}); });
export const getFeedPanelHeaderText = (
threadType: ThreadType = ThreadType.Conversation
) => {
switch (threadType) {
case ThreadType.Announcement:
return 'Announcement';
case ThreadType.Task:
return 'Task';
case ThreadType.Conversation:
default:
return 'Conversation';
}
};