diff --git a/frontend/src/public/components/Highlights/FeedItemComment.tsx b/frontend/src/public/components/Highlights/FeedItemComment.tsx new file mode 100644 index 000000000..691744c6a --- /dev/null +++ b/frontend/src/public/components/Highlights/FeedItemComment.tsx @@ -0,0 +1,64 @@ +import React, { useEffect, useRef, useState } from 'react'; +import classnames from 'classnames'; +import { useIntl } from 'react-intl'; + +import { EWorkflowLogEvent } from '../../types/workflow'; +import { isArrayWithItems } from '../../utils/helpers'; +import { Attachments } from '../Attachments'; +import { RichText } from '../RichText'; + +import { Ellipsis } from './Ellipsis'; +import { IFeedItemCommentProps } from './types'; +import { TruncatedContent } from './utils/TruncatedContent'; + +import styles from './FeedItem.css'; + +const MAX_TRUNCATED_COMMENT_HEIGHT = 20 * 5; + +export function FeedItemComment({ + attachments, + isTextExpanded, + onExpand, + task, + text, + type, +}: IFeedItemCommentProps) { + const { formatMessage, messages } = useIntl(); + const commentTextRef = useRef(null); + const [isCommentExpandable, setIsCommentExpandable] = useState(true); + const hasAttachments = Boolean(text && attachments && isArrayWithItems(attachments)); + + useEffect(() => { + const commentTextHeight = commentTextRef.current?.scrollHeight || 0; + + setIsCommentExpandable(commentTextHeight > MAX_TRUNCATED_COMMENT_HEIGHT || hasAttachments); + }, [attachments, hasAttachments, text]); + + if (!text) { + return null; + } + + const isTruncated = !isTextExpanded && isCommentExpandable; + + return ( + <> +
+ + {type === EWorkflowLogEvent.TaskRevert + ? formatMessage({ id: 'task.log-returned' }, { taskName: task?.name }) + : messages['general.comment']} + + + + + + +
+ {hasAttachments && !isTruncated && } + {isTruncated && } + + ); +} diff --git a/frontend/src/public/components/Highlights/FeedItemHeader.tsx b/frontend/src/public/components/Highlights/FeedItemHeader.tsx index 7d8a595df..04477fcc5 100644 --- a/frontend/src/public/components/Highlights/FeedItemHeader.tsx +++ b/frontend/src/public/components/Highlights/FeedItemHeader.tsx @@ -1,27 +1,13 @@ -import * as React from 'react'; -import { useState, useEffect, useRef } from 'react'; -import classnames from 'classnames'; +import React, { useState } from 'react'; import { useIntl } from 'react-intl'; -import { RichText } from '../RichText'; -import { Attachments } from '../Attachments'; import { EWorkflowLogEvent } from '../../types/workflow'; -import { IFieldsetRuntime } from '../../types/fieldset'; -import { EExtraFieldType, ETemplateOwnerType, IExtraField } from '../../types/template'; -import { IHighlightsItem } from '../../types/highlights'; -import { isArrayWithItems } from '../../utils/helpers'; -import { EKickoffOutputsViewModes, KickoffOutputs } from '../KickoffOutputs'; -import { UserData } from '../UserData'; -import { getUserFullName } from '../../utils/users'; -import UserDataWithGroup from '../UserDataWithGroup'; import { getSnoozedUntilDate } from '../../utils/dateTime'; -import { Ellipsis } from './Ellipsis'; -import { TruncatedContent } from './utils/TruncatedContent'; - -import styles from './FeedItem.css'; - -interface IFeedItemHeaderProps extends IHighlightsItem {} +import { FeedItemComment } from './FeedItemComment'; +import { FeedItemOutputs } from './FeedItemOutputs'; +import { PerformerChange } from './PerformerChange'; +import { IFeedItemHeaderProps } from './types'; export function FeedItemHeader({ attachments, @@ -33,243 +19,60 @@ export function FeedItemHeader({ targetUserId, targetGroupId, }: IFeedItemHeaderProps) { - const { messages, formatMessage } = useIntl(); - const commentTextRef = useRef(null); - + const { formatMessage } = useIntl(); const [isTextExpanded, setIsTextExpanded] = useState(false); - const [isCommentExpandable, setIsCommentExpandable] = useState(true); - - useEffect(() => { - const commentTextHeigh = commentTextRef.current?.offsetHeight || 0; - - setIsCommentExpandable(commentTextHeigh > MAX_TRUNCATED_COMMENT_HEIGHT || hasAttachments); - }, [commentTextRef.current]); - - const hasAttachments = Boolean(text && attachments && isArrayWithItems(attachments)); - const hasCommentText = Boolean(text); - const MAX_TRUNCATED_COMMENT_HEIGHT = 20 * 5; - - const renderElipsis = () => { - return setIsTextExpanded(true)} />; - }; - - const renderOutputsContents = () => { - const { output: taskOutput, fieldsets: taskFieldsets } = task || {}; - const { output: kickoffOutput, fieldsets: kickoffFieldsets } = kickoff || {}; - - if ( - !isArrayWithItems(taskOutput) && - !isArrayWithItems(kickoffOutput) && - !isArrayWithItems(taskFieldsets) && - !isArrayWithItems(kickoffFieldsets) - ) { - return null; - } - - const OUTPUTS_MAP: { [key in EWorkflowLogEvent]?: IExtraField[] } = { - [EWorkflowLogEvent.WorkflowRun]: kickoffOutput, - [EWorkflowLogEvent.WorkflowComplete]: taskOutput, - [EWorkflowLogEvent.TaskComplete]: taskOutput, - [EWorkflowLogEvent.WorkflowsReturned]: taskOutput, - [EWorkflowLogEvent.TaskRevert]: taskOutput, - }; - - const FIELDSETS_MAP: { [key in EWorkflowLogEvent]?: IFieldsetRuntime[] } = { - [EWorkflowLogEvent.WorkflowRun]: kickoffFieldsets, - [EWorkflowLogEvent.WorkflowComplete]: taskFieldsets, - [EWorkflowLogEvent.TaskComplete]: taskFieldsets, - [EWorkflowLogEvent.WorkflowsReturned]: taskFieldsets, - [EWorkflowLogEvent.TaskRevert]: taskFieldsets, - }; - - const outputs = OUTPUTS_MAP[type]; - const fieldsets = FIELDSETS_MAP[type]; - - if (!isArrayWithItems(outputs) && !isArrayWithItems(fieldsets)) { - return null; - } - - const filterField = (output: IExtraField) => { - const value = output.type === EExtraFieldType.User ? output.userId || output.groupId : output.value; - return value || output.attachments?.length; - }; - - const filteredOutputs = (outputs || []).filter(filterField); - - const filteredFieldsets = (fieldsets || []) - .map((fs) => ({ - ...fs, - fields: fs.fields.filter(filterField), - })) - .filter((fs) => fs.fields.length > 0); - - return ( - <> - setIsTextExpanded(true); + + switch (type) { + case EWorkflowLogEvent.TaskComplete: + case EWorkflowLogEvent.WorkflowRun: + case EWorkflowLogEvent.WorkflowComplete: + case EWorkflowLogEvent.WorkflowsReturned: + return ( + - {filteredOutputs.length + filteredFieldsets.flatMap((fs) => fs.fields).length > 1 && - !isTextExpanded && - renderElipsis()} - - ); - }; - - const renderAttachments = () => { - if (!text || !hasAttachments) { - return null; - } - - return ( - <> - - {attachments.length > 1 && renderElipsis()} - - ); - }; - - const renderCommentContent = (params: { isTruncated: boolean }) => { - if (!text) { - return null; - } - - const { isTruncated } = params; - - return ( - <> - {hasCommentText && ( -
- - {type === EWorkflowLogEvent.TaskRevert - ? formatMessage({ id: 'task.log-returned' }, { taskName: task?.name }) - : messages['general.comment']} - - - - - - -
- )} - {hasAttachments && !isTruncated && } - {isTruncated && renderElipsis()} - - ); - }; - - const renderTaskCommentContent = () => { - if (!text) { - return null; - } - - const hasOnlyAttachments = hasAttachments && !text; - - if (hasOnlyAttachments) { - return renderAttachments(); - } - - const shoudShowTruncatedContent = !isTextExpanded && isCommentExpandable; - - return renderCommentContent({ isTruncated: shoudShowTruncatedContent }); - }; - - const renderAddedPerformer = () => { - if (!targetUserId) { - return null; - } - - return ( -
- {formatMessage({ id: 'task.log-added-performer' })} - - {(user) => { - if (!user) { - return null; - } - - return {getUserFullName(user, { withAtSign: true })}; - }} - -
- ); - }; - - const renderRemovedPerformer = () => { - if (!targetUserId) { - return null; - } + ); + + case EWorkflowLogEvent.TaskComment: + case EWorkflowLogEvent.WorkflowFinished: + case EWorkflowLogEvent.TaskRevert: + return ( + + ); + + case EWorkflowLogEvent.AddedPerformer: + case EWorkflowLogEvent.RemovedPerformer: + case EWorkflowLogEvent.AddedPerformerGroup: + case EWorkflowLogEvent.RemovedPerformerGroup: + return ( + + ); - return ( -
- {formatMessage({ id: 'task.log-removed-performer' })} - - {(user) => { - if (!user) { - return null; - } + case EWorkflowLogEvent.WorkflowSnoozedManually: + return ( + <>{formatMessage({ id: 'workflows.event-snoozed-until' }, { date: getSnoozedUntilDate(delay || null) })} + ); - return {getUserFullName(user, { withAtSign: true })}; - }} - -
- ); - }; + case EWorkflowLogEvent.WorkflowResumed: + return <>{formatMessage({ id: 'workflows.event-resumed' })}; - const renderAddedPerformerGroup = () => { - if (!targetGroupId) { + default: return null; - } - - return ( -
- {formatMessage({ id: 'task.log-added-performer-group' })} - - {(group) => {group.firstName}} - -
- ); - }; - - const renderRemovedPerformerGroup = () => { - if (!targetGroupId) { - return null; - } - - return ( -
- {formatMessage({ id: 'task.log-removed-performer-group' })} - - {(group) => {group.firstName}} - -
- ); - }; - - const EVENT_CONTENT_MAP: { [key in EWorkflowLogEvent]?: JSX.Element | null } = { - [EWorkflowLogEvent.TaskComplete]: renderOutputsContents(), - [EWorkflowLogEvent.TaskComment]: renderTaskCommentContent(), - [EWorkflowLogEvent.WorkflowRun]: renderOutputsContents(), - [EWorkflowLogEvent.WorkflowFinished]: renderTaskCommentContent(), - [EWorkflowLogEvent.WorkflowComplete]: renderOutputsContents(), - [EWorkflowLogEvent.WorkflowsReturned]: renderOutputsContents(), - [EWorkflowLogEvent.TaskRevert]: renderTaskCommentContent(), - [EWorkflowLogEvent.AddedPerformer]: renderAddedPerformer(), - [EWorkflowLogEvent.RemovedPerformer]: renderRemovedPerformer(), - [EWorkflowLogEvent.AddedPerformerGroup]: renderAddedPerformerGroup(), - [EWorkflowLogEvent.RemovedPerformerGroup]: renderRemovedPerformerGroup(), - [EWorkflowLogEvent.WorkflowSnoozedManually]: ( - <>{formatMessage({ id: 'workflows.event-snoozed-until' }, { date: getSnoozedUntilDate(delay || null) })} - ), - [EWorkflowLogEvent.WorkflowResumed]: <>{formatMessage({ id: 'workflows.event-resumed' })}, - }; - - const FeedItemHeaderComponent = EVENT_CONTENT_MAP[type] ?? null; - - return FeedItemHeaderComponent; + } } diff --git a/frontend/src/public/components/Highlights/FeedItemOutputs.tsx b/frontend/src/public/components/Highlights/FeedItemOutputs.tsx new file mode 100644 index 000000000..ff27aa66b --- /dev/null +++ b/frontend/src/public/components/Highlights/FeedItemOutputs.tsx @@ -0,0 +1,57 @@ +import React from 'react'; + +import { EExtraFieldType, IExtraField } from '../../types/template'; +import { EWorkflowLogEvent } from '../../types/workflow'; +import { isArrayWithItems } from '../../utils/helpers'; +import { EKickoffOutputsViewModes, KickoffOutputs } from '../KickoffOutputs'; + +import { Ellipsis } from './Ellipsis'; +import { IFeedItemOutputsProps } from './types'; + +export function FeedItemOutputs({ kickoff, isTextExpanded, onExpand, task, type }: IFeedItemOutputsProps) { + const outputsByEvent: { [key in EWorkflowLogEvent]?: IExtraField[] } = { + [EWorkflowLogEvent.WorkflowRun]: kickoff?.output, + [EWorkflowLogEvent.WorkflowComplete]: task?.output, + [EWorkflowLogEvent.TaskComplete]: task?.output, + [EWorkflowLogEvent.WorkflowsReturned]: task?.output, + [EWorkflowLogEvent.TaskRevert]: task?.output, + }; + const fieldsetsByEvent = { + [EWorkflowLogEvent.WorkflowRun]: kickoff?.fieldsets, + [EWorkflowLogEvent.WorkflowComplete]: task?.fieldsets, + [EWorkflowLogEvent.TaskComplete]: task?.fieldsets, + [EWorkflowLogEvent.WorkflowsReturned]: task?.fieldsets, + [EWorkflowLogEvent.TaskRevert]: task?.fieldsets, + }; + const outputs = outputsByEvent[type] ?? []; + const fieldsets = fieldsetsByEvent[type as keyof typeof fieldsetsByEvent] ?? []; + + if (!isArrayWithItems(outputs) && !isArrayWithItems(fieldsets)) { + return null; + } + + const hasValue = (output: IExtraField) => { + const value = output.type === EExtraFieldType.User ? output.userId || output.groupId : output.value; + const hasFileValue = output.type === EExtraFieldType.File && Boolean(output.markdownValue); + + return value || output.attachments?.length || hasFileValue; + }; + const filteredOutputs = outputs.filter(hasValue); + const filteredFieldsets = fieldsets + .map((fieldset) => ({ ...fieldset, fields: fieldset.fields.filter(hasValue) })) + .filter((fieldset) => fieldset.fields.length > 0); + + return ( + <> + + {filteredOutputs.length + filteredFieldsets.flatMap((fieldset) => fieldset.fields).length > 1 + && !isTextExpanded + && } + + ); +} diff --git a/frontend/src/public/components/Highlights/PerformerChange.tsx b/frontend/src/public/components/Highlights/PerformerChange.tsx new file mode 100644 index 000000000..dba6a6d0a --- /dev/null +++ b/frontend/src/public/components/Highlights/PerformerChange.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { useIntl } from 'react-intl'; + +import { ETemplateOwnerType } from '../../types/template'; +import { EWorkflowLogEvent } from '../../types/workflow'; +import { getUserFullName } from '../../utils/users'; +import { UserData } from '../UserData'; +import UserDataWithGroup from '../UserDataWithGroup'; + +import { IPerformerChangeProps } from './types'; + +import styles from './FeedItem.css'; + +export function PerformerChange({ targetGroupId, targetUserId, type }: IPerformerChangeProps) { + const { formatMessage } = useIntl(); + const isGroup = + type === EWorkflowLogEvent.AddedPerformerGroup || type === EWorkflowLogEvent.RemovedPerformerGroup; + + if (isGroup) { + if (!targetGroupId) { + return null; + } + + const messageId = + type === EWorkflowLogEvent.AddedPerformerGroup + ? 'task.log-added-performer-group' + : 'task.log-removed-performer-group'; + + return ( +
+ {formatMessage({ id: messageId })} + + {(group) => {group.firstName}} + +
+ ); + } + + if (!targetUserId) { + return null; + } + + const messageId = + type === EWorkflowLogEvent.AddedPerformer ? 'task.log-added-performer' : 'task.log-removed-performer'; + + return ( +
+ {formatMessage({ id: messageId })} + + {(user) => { + if (!user) { + return null; + } + + return {getUserFullName(user, { withAtSign: true })}; + }} + +
+ ); +} diff --git a/frontend/src/public/components/Highlights/__tests__/FeedItemComment.test.tsx b/frontend/src/public/components/Highlights/__tests__/FeedItemComment.test.tsx new file mode 100644 index 000000000..f3ff59649 --- /dev/null +++ b/frontend/src/public/components/Highlights/__tests__/FeedItemComment.test.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { render, screen, waitFor } from '@testing-library/react'; + +import { EWorkflowLogEvent } from '../../../types/workflow'; +import { FeedItemComment } from '../FeedItemComment'; +import { IFeedItemCommentProps } from '../types'; + +jest.mock('../../RichText', () => ({ + RichText: ({ text }: { text: string }) => {text}, +})); + +jest.mock('../../Attachments', () => ({ + Attachments: () => null, +})); + +jest.mock('../Ellipsis', () => ({ + Ellipsis: () => , +})); + +jest.mock('../utils/TruncatedContent', () => ({ + TruncatedContent: ({ children }: { children: React.ReactNode }) => children, +})); + +describe('FeedItemComment', () => { + it('remeasures expandability when comment text changes', async () => { + let commentHeight = 0; + const scrollHeightMock = jest + .spyOn(HTMLElement.prototype, 'scrollHeight', 'get') + .mockImplementation(() => commentHeight); + const props: IFeedItemCommentProps = { + attachments: [], + isTextExpanded: false, + onExpand: jest.fn(), + task: null, + text: 'Short comment', + type: EWorkflowLogEvent.TaskComment, + }; + const { rerender } = render(); + + await waitFor(() => expect(screen.queryByTestId('ellipsis')).not.toBeInTheDocument()); + + commentHeight = 120; + rerender(); + + await waitFor(() => expect(screen.getByTestId('ellipsis')).toBeInTheDocument()); + scrollHeightMock.mockRestore(); + }); +}); diff --git a/frontend/src/public/components/Highlights/types.ts b/frontend/src/public/components/Highlights/types.ts index 04b2a32fc..c77d77264 100644 --- a/frontend/src/public/components/Highlights/types.ts +++ b/frontend/src/public/components/Highlights/types.ts @@ -1,8 +1,8 @@ import { ChangeEvent } from 'react'; -import { THighlightsDateFilter, EHighlightsDateFilter } from '../../types/highlights'; -import { TUserListItem } from '../../types/user'; +import { EHighlightsDateFilter, IHighlightsItem, THighlightsDateFilter } from '../../types/highlights'; import { ITemplateTitleBaseWithCount } from '../../types/template'; +import { TUserListItem } from '../../types/user'; export interface IDateFilterProps { endDate: Date | null; @@ -34,3 +34,20 @@ export interface ITemplatesFilterProps { changeTemplatesSearchText(e: ChangeEvent): void; changeTemplatesFilter(templateId: number): (e: ChangeEvent) => void; } + +export interface IFeedItemHeaderProps extends IHighlightsItem {} + +export interface IFeedItemCommentProps + extends Pick { + isTextExpanded: boolean; + onExpand: () => void; +} + +export interface IFeedItemOutputsProps extends Pick { + kickoff: IFeedItemHeaderProps['workflow']['kickoff']; + isTextExpanded: boolean; + onExpand: () => void; +} + +export interface IPerformerChangeProps + extends Pick {} diff --git a/frontend/src/public/components/RichEditor/plugins/CopyAttachmentPlugin/__tests__/CopyAttachmentPlugin.test.tsx b/frontend/src/public/components/RichEditor/plugins/CopyAttachmentPlugin/__tests__/CopyAttachmentPlugin.test.tsx index f37e33eb5..7ddcf153d 100644 --- a/frontend/src/public/components/RichEditor/plugins/CopyAttachmentPlugin/__tests__/CopyAttachmentPlugin.test.tsx +++ b/frontend/src/public/components/RichEditor/plugins/CopyAttachmentPlugin/__tests__/CopyAttachmentPlugin.test.tsx @@ -1,4 +1,8 @@ -import * as React from 'react'; +import React, { + createRef, + type MutableRefObject, + type ReactElement, +} from 'react'; import { render, waitFor, act } from '@testing-library/react'; import { LexicalComposer } from '@lexical/react/LexicalComposer'; import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'; @@ -13,7 +17,6 @@ import { $createRangeSelection, $setSelection, } from 'lexical'; -import type { MutableRefObject } from 'react'; import type { LexicalEditor } from 'lexical'; import { CopyAttachmentPlugin } from '../CopyAttachmentPlugin'; import { SetEditorRefPlugin } from '../../SetEditorRefPlugin'; @@ -31,23 +34,18 @@ interface SerializedNode { } function collectTypes(nodes: SerializedNode[]): string[] { - const types: string[] = []; - for (const n of nodes) { - types.push(n.type); - if (n.children) types.push(...collectTypes(n.children)); - } - return types; + return nodes.reduce((types, node) => [ + ...types, + node.type, + ...(node.children ? collectTypes(node.children) : []), + ], []); } function findNodeDeep(nodes: SerializedNode[], type: string): SerializedNode | undefined { - for (const n of nodes) { - if (n.type === type) return n; - if (n.children) { - const found = findNodeDeep(n.children, type); - if (found) return found; - } - } - return undefined; + return nodes.reduce((found, node) => { + if (found || node.type === type) return found ?? node; + return node.children ? findNodeDeep(node.children, type) : undefined; + }, undefined); } beforeAll(() => { @@ -102,7 +100,7 @@ function TestHarness({ editorRef, }: { editorRef: MutableRefObject; -}): React.ReactElement { +}): ReactElement { return ( { - const editorRef = React.createRef() as MutableRefObject; + const editorRef = createRef() as MutableRefObject; render(); await waitFor(() => expect(editorRef.current).not.toBeNull()); return editorRef.current!; @@ -240,8 +238,17 @@ describe('CopyAttachmentPlugin', () => { const event = createClipboardEvent('cut'); let result: boolean | undefined; - act(() => { + await act(async () => { + let removeUpdateListener = () => {}; + const reconciled = new Promise((resolve) => { + removeUpdateListener = editor.registerUpdateListener(() => { + removeUpdateListener(); + resolve(); + }); + }); + result = editor.dispatchCommand(CUT_COMMAND, event); + await reconciled; }); expect(result).toBe(true); diff --git a/frontend/src/public/components/TaskCard/TaskCardHeader.tsx b/frontend/src/public/components/TaskCard/TaskCardHeader.tsx index 0fab0eae1..feae88e20 100644 --- a/frontend/src/public/components/TaskCard/TaskCardHeader.tsx +++ b/frontend/src/public/components/TaskCard/TaskCardHeader.tsx @@ -2,9 +2,9 @@ import React, { MouseEvent, useRef } from 'react'; import { useIntl } from 'react-intl'; import { Link } from 'react-router-dom'; -import { history } from '../../utils/history'; -import { getTaskDetailRoute, getWorkflowDetailedRoute, isTaskDetailRoute } from '../../utils/routes'; import { sanitizeText } from '../../utils/strings'; +import { getTaskDetailRoute, getWorkflowDetailedRoute, isTaskDetailRoute } from '../../utils/routes'; +import { history } from '../../utils/history'; import { Header } from '../UI/Typeography/Header'; import { Tooltip } from '../UI'; import { DateFormat } from '../UI/DateFormat'; @@ -15,24 +15,40 @@ import styles from './TaskCard.css'; export function TaskCardHeader({ task, viewMode, workflowLog, openWorkflowLogPopup }: TTaskCardHeaderProps) { const { formatMessage } = useIntl(); const workflowLinkRef = useRef(null); - const redirectToWorkflowUrl = workflowLog.workflowId ? getWorkflowDetailedRoute(workflowLog.workflowId) : '#'; - const redirectToTaskUrl = getTaskDetailRoute(task.id); + const { + name, + id, + dateStarted, + workflow: { name: workflowName, templateName }, + isUrgent, + } = task; + const redirectToWorkflowUrl = workflowLog?.workflowId ? getWorkflowDetailedRoute(workflowLog.workflowId) : '#'; + const redirectToTaskUrl = getTaskDetailRoute(id); const showLinkToTaskDetail = !isTaskDetailRoute(history.location.pathname); const handleOpenWorkflowPopup = (workflowId: number | null) => (event: MouseEvent) => { event.preventDefault(); - if (workflowId) openWorkflowLogPopup({ workflowId }); + if (workflowId) { + openWorkflowLogPopup({ workflowId }); + } }; if (viewMode === ETaskCardViewMode.Guest) { - return
{task.name}
; + return ( +
+ {name} +
+ ); } return ( <>
- {task.workflow.templateName} + {templateName}
- + - {sanitizeText(task.workflow.name)} + {sanitizeText(workflowName)}
+
- {task.isUrgent && ( + {isUrgent ? (
{formatMessage({ id: 'workflows.card-urgent' })}
- )} - {showLinkToTaskDetail - ? {sanitizeText(task.name)} - : sanitizeText(task.name)} + ) : null} + {showLinkToTaskDetail ? {sanitizeText(name)} : sanitizeText(name)}
- + + + ); } diff --git a/frontend/src/public/components/TaskCard/TaskWorkflowLog.tsx b/frontend/src/public/components/TaskCard/TaskWorkflowLog.tsx index 786e3aae1..1c04dbca2 100644 --- a/frontend/src/public/components/TaskCard/TaskWorkflowLog.tsx +++ b/frontend/src/public/components/TaskCard/TaskWorkflowLog.tsx @@ -20,7 +20,9 @@ export function TaskWorkflowLog({ changeTaskWorkflowLogViewSettings, toggleTaskSkippedTasksVisibility, }: TTaskWorkflowLogProps) { - if (isWorkflowLoading) return ; + if (isWorkflowLoading) { + return ; + } return ( ({ ExtraFieldIntl: jest.fn(() =>
), })); -const mockExtraFieldIntl = ExtraFieldIntl as unknown as jest.Mock; -const mockButton = jest.fn(); - jest.mock('../utils/storageOutputs', () => ({ getOutputFromStorage: jest.fn(() => undefined), addOrUpdateStorageOutput: jest.fn(), - fieldsetsStorage: { get: jest.fn(), save: jest.fn() }, + removeOutputFromLocalStorage: jest.fn(), + outputStorage: { getEntry: jest.fn() }, + fieldsetsStorage: { get: jest.fn(), getEntry: jest.fn(), save: jest.fn() }, })); jest.mock('../../../utils/autoFocusFirstField', () => ({ @@ -38,7 +36,7 @@ jest.mock('../../../redux/selectors/groups', () => ({ getRegularGroupsList: () => [{ id: 5, name: 'Group Five', type: 'regular' }], })); -const mockUsersDropdown = jest.fn((_props?: unknown) => null); +const mockUsersDropdown = jest.fn(); jest.mock('../../UI/form/UsersDropdown', () => ({ UsersDropdown: (props: unknown) => { @@ -62,7 +60,7 @@ jest.mock('../../Workflows/WorkflowLog/WorkflowLogSkeleton', () => ({ })); jest.mock('../GuestsController', () => ({ - GuestController: React.forwardRef(() => null), + GuestController: forwardRef(() => null), })); jest.mock('../SubWorkflows', () => ({ @@ -82,13 +80,12 @@ let returnModalOnConfirm: ((comment: string) => void) | undefined; jest.mock('../ReturnModal', () => ({ ReturnModal: ({ onConfirm }: { onConfirm: (comment: string) => void }) => { returnModalOnConfirm = onConfirm; - return null; }, })); jest.mock('react-router-dom', () => ({ - Link: ({ children }: { children: React.ReactNode }) => {children}, + Link: ({ children }: { children: ReactNode }) => {children}, })); jest.mock('../../RichText', () => ({ @@ -129,18 +126,17 @@ jest.mock('../checklist', () => ({ })); jest.mock('../../UI', () => ({ - Tooltip: ({ children }: { children: React.ReactNode }) =>
{children}
, + Tooltip: ({ children }: { children: ReactNode }) =>
{children}
, })); jest.mock('../../UI/Typeography/Header', () => ({ - Header: ({ children }: { children: React.ReactNode }) =>

{children}

, + Header: ({ children }: { children: ReactNode }) =>

{children}

, })); jest.mock('../../UI/Buttons/Button', () => ({ - Button: (props: { disabled?: boolean }) => { + Button: (props: { disabled?: boolean; onClick?(): void }) => { mockButton(props); - - return