From 4b34cd6e1630671a5689697f38b9ecd763dbc16d Mon Sep 17 00:00:00 2001 From: Jorge Padilla Date: Mon, 19 Feb 2024 11:00:01 -0500 Subject: [PATCH] fix(frontend): fix bug in test specs virtual list (#3657) --- web/src/components/Layout/Layout.styled.ts | 2 +- .../TestResults/TestResults.styled.ts | 2 + .../components/TestSpec/TestSpec.styled.ts | 3 + .../components/TestSpecs/TestSpecs.styled.ts | 19 +++ web/src/components/TestSpecs/TestSpecs.tsx | 90 +++++++---- .../Timeline/BaseSpanNode/BaseSpanNodeV2.tsx | 62 -------- .../Timeline/BaseSpanNode/ConnectorV2.tsx | 56 ------- .../components/Timeline/SpanNodeFactoryV2.tsx | 30 ---- .../components/Timeline/TimelineV2.styled.ts | 150 ------------------ .../components/Timeline/TimelineV2.tsx | 37 ----- .../TraceSpanNode/TraceSpanNodeV2.tsx | 19 --- 11 files changed, 87 insertions(+), 383 deletions(-) delete mode 100644 web/src/components/Visualization/components/Timeline/BaseSpanNode/BaseSpanNodeV2.tsx delete mode 100644 web/src/components/Visualization/components/Timeline/BaseSpanNode/ConnectorV2.tsx delete mode 100644 web/src/components/Visualization/components/Timeline/SpanNodeFactoryV2.tsx delete mode 100644 web/src/components/Visualization/components/Timeline/TimelineV2.styled.ts delete mode 100644 web/src/components/Visualization/components/Timeline/TimelineV2.tsx delete mode 100644 web/src/components/Visualization/components/Timeline/TraceSpanNode/TraceSpanNodeV2.tsx diff --git a/web/src/components/Layout/Layout.styled.ts b/web/src/components/Layout/Layout.styled.ts index 2650d36d4a..cf4d327bbb 100644 --- a/web/src/components/Layout/Layout.styled.ts +++ b/web/src/components/Layout/Layout.styled.ts @@ -4,13 +4,13 @@ import styled, {css} from 'styled-components'; export const Content = styled(LayoutAntd.Content)<{$hasMenu: boolean}>` display: flex; flex-direction: column; - padding-bottom: 40px; ${({$hasMenu}) => $hasMenu && css` height: 100%; overflow-y: scroll; + padding-bottom: 40px; `} `; diff --git a/web/src/components/TestResults/TestResults.styled.ts b/web/src/components/TestResults/TestResults.styled.ts index 22347a5138..14fe6f1a09 100644 --- a/web/src/components/TestResults/TestResults.styled.ts +++ b/web/src/components/TestResults/TestResults.styled.ts @@ -2,6 +2,8 @@ import {Button, Typography} from 'antd'; import styled from 'styled-components'; export const Container = styled.div` + display: flex; + flex-direction: column; height: 100%; position: relative; `; diff --git a/web/src/components/TestSpec/TestSpec.styled.ts b/web/src/components/TestSpec/TestSpec.styled.ts index dd5a301bef..dc95ed2ecd 100644 --- a/web/src/components/TestSpec/TestSpec.styled.ts +++ b/web/src/components/TestSpec/TestSpec.styled.ts @@ -57,7 +57,10 @@ export const Selector = styled.div` export const Title = styled(Typography.Text)` color: ${({theme}) => theme.color.text}; + font-family: monospace; font-size: ${({theme}) => theme.size.md}; + line-height: 1.4; + padding: 4px 0; `; export const WarningIcon = styled(InfoCircleFilled)` diff --git a/web/src/components/TestSpecs/TestSpecs.styled.ts b/web/src/components/TestSpecs/TestSpecs.styled.ts index 25433c3f84..59a1bbc89d 100644 --- a/web/src/components/TestSpecs/TestSpecs.styled.ts +++ b/web/src/components/TestSpecs/TestSpecs.styled.ts @@ -29,3 +29,22 @@ export const EmptyTitle = styled(Typography.Title).attrs({level: 3})``; export const SnippetsContainer = styled.div` margin: 16px 0; `; + +export const ListContainer = styled.div` + flex: 1; +`; + +export const HiddenElementContainer = styled.div` + position: absolute; + visibility: hidden; + z-index: -1; +`; + +export const HiddenElement = styled.div` + font-family: monospace; + font-size: ${({theme}) => theme.size.md}; + line-height: 1.4; + padding: 4px 96px 76px 4px; + visibility: hidden; + width: 100%; +`; diff --git a/web/src/components/TestSpecs/TestSpecs.tsx b/web/src/components/TestSpecs/TestSpecs.tsx index 83aa70b175..50fd009da3 100644 --- a/web/src/components/TestSpecs/TestSpecs.tsx +++ b/web/src/components/TestSpecs/TestSpecs.tsx @@ -1,8 +1,12 @@ import TestSpec from 'components/TestSpec'; -import AutoSizer, {Size} from 'react-virtualized-auto-sizer'; -import {FixedSizeList as List} from 'react-window'; import AssertionResults, {TAssertionResultEntry} from 'models/AssertionResults.model'; +import {useCallback, useRef} from 'react'; +import AutoSizer, {Size} from 'react-virtualized-auto-sizer'; +import {VariableSizeList as List} from 'react-window'; +import {useAppSelector} from 'redux/hooks'; +import TestSpecsSelectors from 'selectors/TestSpecs.selectors'; import Empty from './Empty'; +import * as S from './TestSpecs.styled'; interface IProps { assertionResults?: AssertionResults; @@ -13,37 +17,67 @@ interface IProps { } const TestSpecs = ({assertionResults, onDelete, onEdit, onOpen, onRevert}: IProps) => { + const hiddenElementRef = useRef(null); + const specs = useAppSelector(state => TestSpecsSelectors.selectSpecs(state)); + + const getItemSize = useCallback( + index => { + const item = assertionResults?.resultList?.[index]; + const selector = item?.selector ?? ''; + const {name = ''} = specs.find(spec => spec.selector === selector) ?? {}; + const label = name || selector || 'All Spans'; + + if (hiddenElementRef.current) { + hiddenElementRef.current.innerText = label; + return hiddenElementRef.current.offsetHeight; + } + + return 0; + }, + [assertionResults?.resultList, specs] + ); + if (!assertionResults?.resultList?.length) { return ; } return ( - - {({height, width}: Size) => ( - - {({index, data}) => { - const specResult = data[index]; - - return specResult.resultList.length ? ( - - ) : null; - }} - - )} - + <> + + + {({height, width}: Size) => ( + + {({index, data, style}) => { + const specResult = data[index]; + + return specResult.resultList.length ? ( +
+ +
+ ) : null; + }} +
+ )} +
+
+ + + + + ); }; diff --git a/web/src/components/Visualization/components/Timeline/BaseSpanNode/BaseSpanNodeV2.tsx b/web/src/components/Visualization/components/Timeline/BaseSpanNode/BaseSpanNodeV2.tsx deleted file mode 100644 index 96934f48a1..0000000000 --- a/web/src/components/Visualization/components/Timeline/BaseSpanNode/BaseSpanNodeV2.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import Span from 'models/Span.model'; -import Connector from './ConnectorV2'; -import {IPropsComponent} from '../SpanNodeFactoryV2'; -import {useTimeline} from '../Timeline.provider'; -import * as S from '../TimelineV2.styled'; - -function toPercent(value: number) { - return `${(value * 100).toFixed(1)}%`; -} - -function getHintSide(viewStart: number, viewEnd: number) { - return viewStart > 1 - viewEnd ? 'left' : 'right'; -} - -interface IProps extends IPropsComponent { - span: Span; -} - -const BaseSpanNode = ({index, node, span, style}: IProps) => { - const {collapsedSpans, getScale, matchedSpans, onSpanCollapse, onSpanClick, selectedSpan} = useTimeline(); - const {start: viewStart, end: viewEnd} = getScale(span.startTime, span.endTime); - const hintSide = getHintSide(viewStart, viewEnd); - const isSelected = selectedSpan === node.data.id; - const isMatched = matchedSpans.includes(node.data.id); - const isCollapsed = collapsedSpans.includes(node.data.id); - - return ( -
- onSpanClick(node.data.id)} - $isEven={index % 2 === 0} - $isMatched={isMatched} - $isSelected={isSelected} - > - - - - - {span.name} - - - - - - - - {span.duration} - - - -
- ); -}; - -export default BaseSpanNode; diff --git a/web/src/components/Visualization/components/Timeline/BaseSpanNode/ConnectorV2.tsx b/web/src/components/Visualization/components/Timeline/BaseSpanNode/ConnectorV2.tsx deleted file mode 100644 index cb332a3f84..0000000000 --- a/web/src/components/Visualization/components/Timeline/BaseSpanNode/ConnectorV2.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import {BaseLeftPaddingV2} from 'constants/Timeline.constants'; -import * as S from '../TimelineV2.styled'; - -interface IProps { - hasParent: boolean; - id: string; - isCollapsed: boolean; - nodeDepth: number; - onCollapse(id: string): void; - totalChildren: number; -} - -const Connector = ({hasParent, id, isCollapsed, nodeDepth, onCollapse, totalChildren}: IProps) => { - const leftPadding = nodeDepth * BaseLeftPaddingV2; - - return ( - - {hasParent && ( - <> - - - - )} - - {totalChildren > 0 ? ( - <> - {!isCollapsed && } - - - {totalChildren} - - { - event.stopPropagation(); - onCollapse(id); - }} - /> - - ) : ( - - )} - - {new Array(nodeDepth).fill(0).map((_, index) => { - return ; - })} - - ); -}; - -export default Connector; diff --git a/web/src/components/Visualization/components/Timeline/SpanNodeFactoryV2.tsx b/web/src/components/Visualization/components/Timeline/SpanNodeFactoryV2.tsx deleted file mode 100644 index 9e6aed970c..0000000000 --- a/web/src/components/Visualization/components/Timeline/SpanNodeFactoryV2.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import {NodeTypesEnum} from 'constants/Visualization.constants'; -import {TNode} from 'types/Timeline.types'; -// import TestSpanNode from './TestSpanNode/TestSpanNode'; -import TraceSpanNode from './TraceSpanNode/TraceSpanNodeV2'; - -export interface IPropsComponent { - index: number; - node: TNode; - style: React.CSSProperties; -} - -const ComponentMap: Record React.ReactElement> = { - [NodeTypesEnum.TestSpan]: TraceSpanNode, - [NodeTypesEnum.TraceSpan]: TraceSpanNode, -}; - -interface IProps { - data: TNode[]; - index: number; - style: React.CSSProperties; -} - -const SpanNodeFactory = ({data, ...props}: IProps) => { - const node = data[props.index]; - const Component = ComponentMap[node.type]; - - return ; -}; - -export default SpanNodeFactory; diff --git a/web/src/components/Visualization/components/Timeline/TimelineV2.styled.ts b/web/src/components/Visualization/components/Timeline/TimelineV2.styled.ts deleted file mode 100644 index 827d7b88a3..0000000000 --- a/web/src/components/Visualization/components/Timeline/TimelineV2.styled.ts +++ /dev/null @@ -1,150 +0,0 @@ -import {Typography} from 'antd'; -import {SemanticGroupNames, SemanticGroupNamesToColor} from 'constants/SemanticGroupNames.constants'; -import styled, {css} from 'styled-components'; - -export const Container = styled.div` - padding: 50px 24px 0 24px; -`; - -export const Row = styled.div<{$isEven: boolean; $isMatched: boolean; $isSelected: boolean}>` - background-color: ${({theme, $isEven}) => ($isEven ? theme.color.background : theme.color.white)}; - display: grid; - grid-template-columns: 300px 1fr; - grid-template-rows: 32px; - padding: 0px 16px; - - :hover { - background-color: ${({theme}) => theme.color.backgroundInteractive}; - } - - ${({$isMatched}) => - $isMatched && - css` - background-color: ${({theme}) => theme.color.alertYellow}; - `}; - - ${({$isSelected}) => - $isSelected && - css` - background: rgba(97, 23, 94, 0.1); - - :hover { - background: rgba(97, 23, 94, 0.1); - } - `}; -`; - -export const Col = styled.div` - display: grid; - grid-template-columns: 1fr 8px; -`; - -export const ColDuration = styled.div` - overflow: hidden; - position: relative; -`; - -export const Header = styled.div` - align-items: center; - display: flex; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -`; - -export const NameContainer = styled.div` - overflow: hidden; - text-overflow: ellipsis; -`; - -export const Separator = styled.div` - border-left: 1px solid rgb(222, 227, 236); - cursor: ew-resize; - height: 32px; - padding: 0px 3px; - width: 1px; -`; - -export const Title = styled(Typography.Text)` - color: ${({theme}) => theme.color.text}; - font-size: ${({theme}) => theme.size.sm}; - font-weight: 400; -`; - -export const Connector = styled.svg` - flex-shrink: 0; - overflow: hidden; - overflow-clip-margin: content-box; -`; - -export const SpanBar = styled.div<{$type: SemanticGroupNames}>` - background-color: ${({$type}) => SemanticGroupNamesToColor[$type]}; - border-radius: 3px; - height: 18px; - min-width: 2px; - position: absolute; - top: 7px; -`; - -export const SpanBarLabel = styled.div<{$side: 'left' | 'right'}>` - color: ${({theme}) => theme.color.textSecondary}; - font-size: ${({theme}) => theme.size.xs}; - padding: 1px 4px 0 4px; - position: absolute; - - ${({$side}) => - $side === 'left' - ? css` - right: 100%; - ` - : css` - left: 100%; - `}; -`; - -export const TextConnector = styled.text<{$isActive?: boolean}>` - fill: ${({theme, $isActive}) => ($isActive ? theme.color.white : theme.color.text)}; - font-size: ${({theme}) => theme.size.xs}; -`; - -export const CircleDot = styled.circle` - fill: ${({theme}) => theme.color.textSecondary}; - stroke-width: 2; - stroke: ${({theme}) => theme.color.white}; -`; - -export const LineBase = styled.line` - stroke: ${({theme}) => theme.color.borderLight}; -`; - -export const RectBase = styled.rect<{$isActive?: boolean}>` - fill: ${({theme, $isActive}) => ($isActive ? theme.color.primary : theme.color.white)}; - stroke: ${({theme}) => theme.color.textSecondary}; -`; - -export const RectBaseTransparent = styled(RectBase)` - cursor: pointer; - fill: transparent; -`; - -export const HeaderRow = styled.div` - background-color: ${({theme}) => theme.color.white}; - display: grid; - grid-template-columns: 300px 1fr; - grid-template-rows: 32px; - padding: 0px 16px; -`; - -export const HeaderContent = styled.div` - align-items: center; - display: flex; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -`; - -export const HeaderTitle = styled(Typography.Title)` - && { - margin: 0; - } -`; diff --git a/web/src/components/Visualization/components/Timeline/TimelineV2.tsx b/web/src/components/Visualization/components/Timeline/TimelineV2.tsx deleted file mode 100644 index b3c72c9078..0000000000 --- a/web/src/components/Visualization/components/Timeline/TimelineV2.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import {NodeTypesEnum} from 'constants/Visualization.constants'; -import Span from 'models/Span.model'; -import {useRef} from 'react'; -import {FixedSizeList as List} from 'react-window'; -import NavigationWrapper from './NavigationWrapper'; -import TimelineProvider from './Timeline.provider'; -import ListWrapper from './ListWrapper'; - -export interface IProps { - nodeType: NodeTypesEnum; - spans: Span[]; - onNavigate(spanId: string): void; - onClick(spanId: string): void; - matchedSpans: string[]; - selectedSpan: string; -} - -const Timeline = ({nodeType, spans, onClick, onNavigate, matchedSpans, selectedSpan}: IProps) => { - const listRef = useRef(null); - - return ( - - - - - ); -}; - -export default Timeline; diff --git a/web/src/components/Visualization/components/Timeline/TraceSpanNode/TraceSpanNodeV2.tsx b/web/src/components/Visualization/components/Timeline/TraceSpanNode/TraceSpanNodeV2.tsx deleted file mode 100644 index 539eda5ae2..0000000000 --- a/web/src/components/Visualization/components/Timeline/TraceSpanNode/TraceSpanNodeV2.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import useSpanData from 'hooks/useSpanData'; -// import Header from './Header'; -import BaseSpanNode from '../BaseSpanNode/BaseSpanNodeV2'; -import {IPropsComponent} from '../SpanNodeFactoryV2'; - -const TraceSpanNode = (props: IPropsComponent) => { - const {node} = props; - const {span} = useSpanData(node.data.id); - - return ( - } - span={span} - /> - ); -}; - -export default TraceSpanNode;