diff --git a/package.json b/package.json index 25eb44e2..5bc809c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "th2-rpt-viewer", - "version": "5.1.28", + "version": "5.1.29", "description": "", "main": "index.tsx", "private": true, diff --git a/src/components/message/message-card/MessageBodyCard.tsx b/src/components/message/message-card/MessageBodyCard.tsx index 4cb45e2c..39d48a0f 100644 --- a/src/components/message/message-card/MessageBodyCard.tsx +++ b/src/components/message/message-card/MessageBodyCard.tsx @@ -26,16 +26,20 @@ import MessageBody, { isMessageValue, isNullValue, } from '../../../models/MessageBody'; +import { HighlightColorManager } from '../../../helpers/highlightUtils'; const BEAUTIFIED_PAD_VALUE = 15; const DEFAULT_HIGHLIGHT_COLOR = '#e2dfdf'; const SELECTED_HIGHLIGHT_COLOR = '#fff'; +const highlightColorManager = new HighlightColorManager(); + interface Props { isBeautified: boolean; body: MessageBody | null; isSelected: boolean; sortOrderItems: string[]; + filterBodyValues: string[] | undefined; } const getSortedFields = (fields: MessageBodyFields, sortOrder: string[]) => { @@ -64,7 +68,13 @@ const getSortedFields = (fields: MessageBodyFields, sortOrder: string[]) => { return [...primarySortedFields, ...secondarySortedFields, ...tertiarySortedFields]; }; -function MessageBodyCard({ isBeautified, body, isSelected, sortOrderItems }: Props) { +function MessageBodyCard({ + isBeautified, + body, + isSelected, + sortOrderItems, + filterBodyValues, +}: Props) { const [areSameContext, highlightSameContext] = React.useState(false); const fields = React.useMemo( @@ -93,6 +103,7 @@ function MessageBodyCard({ isBeautified, body, isSelected, sortOrderItems }: Pro field={value} isBeautified={isBeautified} setIsHighlighted={highlightSameContext} + filterBodyValues={filterBodyValues} /> {isBeautified || idx === arr.length - 1 ? null : ', '} @@ -116,6 +127,7 @@ interface FieldProps { highlightColor: string; primarySort: string[]; renderInfo?: () => React.ReactNode; + filterBodyValues: string[] | undefined; } function MessageBodyCardField(props: FieldProps) { @@ -127,12 +139,23 @@ function MessageBodyCardField(props: FieldProps) { isRoot = true, setIsHighlighted, highlightColor, + filterBodyValues, } = props; const [areSameContext, highlightSameContext] = React.useState(false); const highlight = React.useMemo(() => debounce(() => setIsHighlighted(true), 60), []); + const fieldColor = React.useMemo(() => { + if (!isSimpleValue(field)) { + return ''; + } + if (filterBodyValues?.some(filter => field?.simpleValue?.includes(filter) || false)) { + return highlightColorManager.getHighlightColor(field.simpleValue); + } + return ''; + }, [filterBodyValues]); + const removeHighlight = React.useCallback(() => { highlight.cancel(); setIsHighlighted(false); @@ -148,6 +171,7 @@ function MessageBodyCardField(props: FieldProps) { label={label} isRoot={false} setIsHighlighted={setIsHighlighted} + filterBodyValues={filterBodyValues} /> ); } @@ -165,6 +189,7 @@ function MessageBodyCardField(props: FieldProps) { className='mc-body__field' style={{ display: isBeautified ? 'block' : undefined, + backgroundColor: fieldColor, }}> {isBeautified || idx === arr.length - 1 ? null : ', '} @@ -251,6 +277,7 @@ function MessageBodyCardField(props: FieldProps) { isRoot={false} setIsHighlighted={highlightSameContext} highlightColor={highlightColor} + filterBodyValues={filterBodyValues} /> {isBeautified || idx === arr.length - 1 ? null : ', '} diff --git a/src/components/message/message-card/MessageCard.tsx b/src/components/message/message-card/MessageCard.tsx index 01d69ddb..b1823c94 100644 --- a/src/components/message/message-card/MessageCard.tsx +++ b/src/components/message/message-card/MessageCard.tsx @@ -44,6 +44,7 @@ const MessageCard = observer(({ message, viewType, setViewType }: Props) => { const { messageId } = message; const messagesStore = useMessagesWorkspaceStore(); + const { filterStore } = messagesStore; const messagesDataStore = useMessagesDataStore(); const bookmarksStore = useBookmarksStore(); const { sortOrderItems } = useMessageBodySortStore(); @@ -51,6 +52,8 @@ const MessageCard = observer(({ message, viewType, setViewType }: Props) => { const [isHighlighted, setHighlighted] = React.useState(false); + const [filterBodyValues, setFilterBodyValues] = React.useState(); + const hoverTimeout = React.useRef(); const isContentBeautified = viewType === MessageViewType.FORMATTED; @@ -60,6 +63,12 @@ const MessageCard = observer(({ message, viewType, setViewType }: Props) => { const isSoftFiltered = messagesDataStore.isSoftFiltered.get(messageId); + React.useEffect(() => { + if (filterStore.isMessagesFilterApplied) { + setFilterBodyValues(filterStore.filterParams['body-values']); + } + }, [filterStore.isMessagesFilterApplied]); + React.useEffect(() => { const abortController = new AbortController(); @@ -113,6 +122,7 @@ const MessageCard = observer(({ message, viewType, setViewType }: Props) => { viewType={viewType} setViewType={setViewType} isHighlighted={isHighlighted} + filterBodyValues={filterBodyValues} hoverMessage={hoverMessage} unhoverMessage={unhoverMessage} isBookmarked={isBookmarked} diff --git a/src/components/message/message-card/MessageCardBase.tsx b/src/components/message/message-card/MessageCardBase.tsx index 03cc374b..7347ac18 100644 --- a/src/components/message/message-card/MessageCardBase.tsx +++ b/src/components/message/message-card/MessageCardBase.tsx @@ -39,6 +39,7 @@ export interface MessageCardBaseProps { isExported?: boolean; isExport?: boolean; sortOrderItems?: string[]; + filterBodyValues?: string[] | undefined; viewType: MessageViewType; setViewType: (viewType: MessageViewType) => void; addMessageToExport?: () => void; @@ -61,6 +62,7 @@ const MessageCardBase = React.memo( isExport, sortOrderItems, addMessageToExport, + filterBodyValues, }: MessageCardBaseProps) => { const { messageId, bodyBase64, body } = message; @@ -90,6 +92,7 @@ const MessageCardBase = React.memo( rawContent: bodyBase64, isSelected: isAttached || false, sortOrderItems: sortOrderItems || [], + filterBodyValues, }; const messageCardToolsConfig: MessageCardToolsConfig = { diff --git a/src/components/message/message-card/MessageCardViewTypeRenderer.tsx b/src/components/message/message-card/MessageCardViewTypeRenderer.tsx index cf4314b2..1ef64f93 100644 --- a/src/components/message/message-card/MessageCardViewTypeRenderer.tsx +++ b/src/components/message/message-card/MessageCardViewTypeRenderer.tsx @@ -32,6 +32,7 @@ export type MessageCardViewTypeRendererProps = { isEmbedded?: boolean; isDetailed?: boolean; sortOrderItems: string[]; + filterBodyValues: string[] | undefined; }; const MessageCardViewTypeRenderer = ({ @@ -41,6 +42,7 @@ const MessageCardViewTypeRenderer = ({ isSelected, messageBody, sortOrderItems, + filterBodyValues, }: MessageCardViewTypeRendererProps) => { switch (viewType) { case MessageViewType.FORMATTED: @@ -53,6 +55,7 @@ const MessageCardViewTypeRenderer = ({ isSelected={isSelected} body={messageBody} sortOrderItems={sortOrderItems} + filterBodyValues={filterBodyValues} /> }> ); diff --git a/src/helpers/highlightUtils.ts b/src/helpers/highlightUtils.ts new file mode 100644 index 00000000..ed99d1eb --- /dev/null +++ b/src/helpers/highlightUtils.ts @@ -0,0 +1,46 @@ +/** **************************************************************************** + * Copyright 2020-2020 Exactpro (Exactpro Systems Limited) + * + * 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. + ***************************************************************************** */ + +export function randomHighlightColor(alpha = 0.5): string { + const r = Math.floor(Math.random() * 150 + 50); + const g = Math.floor(Math.random() * 150 + 50); + const b = Math.floor(Math.random() * 150 + 50); + return `rgba(${r},${g},${b},${alpha})`; +} + +export class HighlightColorManager { + private predefinedColors: string[] = [ + 'rgba(255, 0, 0, 0.5)', + 'rgba(0, 255, 0, 0.5)', + 'rgba(0, 255, 255, 0.5)', + 'rgba(255, 255, 0, 0.5)', + 'rgba(255, 0, 255, 0.5)', + ]; + + private colorMap: Map = new Map(); + + public getHighlightColor(value: string): string { + const color = this.colorMap.get(value); + if (color) { + return color; + } + + const newColor = this.predefinedColors.pop() || randomHighlightColor(); + + this.colorMap.set(value, newColor); + return newColor; + } +}