-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(frontend): Adding Span Result Detail Drawer (#3680)
* feat(frontend): * feat(frontend): Adding Span Result Detail Drawer
- Loading branch information
Showing
19 changed files
with
431 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import {useMemo} from 'react'; | ||
import {useTest} from 'providers/Test/Test.provider'; | ||
import {ICheckResult} from 'types/Assertion.types'; | ||
import AssertionService from 'services/Assertion.service'; | ||
import Span from 'models/Span.model'; | ||
import {useTestRun} from 'providers/TestRun/TestRun.provider'; | ||
import * as S from './SpanResultDetail.styled'; | ||
import Assertion from './Assertion'; | ||
import Header from './Header'; | ||
import {useTestSpecs} from '../../providers/TestSpecs/TestSpecs.provider'; | ||
|
||
interface IProps { | ||
span: Span; | ||
checkResults: ICheckResult[]; | ||
onClose(): void; | ||
} | ||
|
||
const Content = ({checkResults, span, onClose}: IProps) => { | ||
const { | ||
run: {id: runId}, | ||
} = useTestRun(); | ||
const { | ||
test: {id: testId}, | ||
} = useTest(); | ||
|
||
const totalPassedChecks = useMemo(() => AssertionService.getTotalPassedSpanChecks(checkResults), [checkResults]); | ||
const {selectedTestSpec} = useTestSpecs(); | ||
|
||
return ( | ||
<> | ||
<Header | ||
span={span} | ||
onClose={onClose} | ||
assertionsFailed={checkResults.length - totalPassedChecks} | ||
assertionsPassed={totalPassedChecks} | ||
/> | ||
<S.AssertionsContainer> | ||
{checkResults.map(checkResult => ( | ||
<Assertion | ||
testId={testId} | ||
runId={runId} | ||
selector={selectedTestSpec?.selector || ''} | ||
check={checkResult} | ||
key={`${checkResult.result.spanId}-${checkResult.assertion}`} | ||
/> | ||
))} | ||
</S.AssertionsContainer> | ||
</> | ||
); | ||
}; | ||
|
||
export default Content; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import Span from 'models/Span.model'; | ||
import {ClockCircleOutlined, SettingOutlined, ToolOutlined} from '@ant-design/icons'; | ||
import {Tooltip} from 'antd'; | ||
import * as STestSpec from 'components/TestSpec/TestSpec.styled'; | ||
import {SemanticGroupNamesToText} from 'constants/SemanticGroupNames.constants'; | ||
import * as SSpanNode from 'components/Visualization/components/DAG/BaseSpanNode/BaseSpanNode.styled'; | ||
import * as S from './SpanResultDetail.styled'; | ||
|
||
interface IProps { | ||
assertionsFailed: number; | ||
assertionsPassed: number; | ||
span: Span; | ||
} | ||
|
||
const Detail = ({ | ||
assertionsFailed, | ||
assertionsPassed, | ||
span: {duration, name, id, service, kind, system, type}, | ||
}: IProps) => { | ||
return ( | ||
<> | ||
<S.DetailsWrapper> | ||
<S.SpanHeaderContainer> | ||
<SSpanNode.BadgeType count={SemanticGroupNamesToText[type]} $type={type} /> | ||
<Tooltip title={name}> | ||
<S.HeaderTitle level={3}>{name}</S.HeaderTitle> | ||
</Tooltip> | ||
</S.SpanHeaderContainer> | ||
<div> | ||
{Boolean(assertionsPassed) && ( | ||
<STestSpec.HeaderDetail> | ||
<STestSpec.HeaderDot $passed /> | ||
{assertionsPassed} | ||
</STestSpec.HeaderDetail> | ||
)} | ||
{Boolean(assertionsFailed) && ( | ||
<STestSpec.HeaderDetail> | ||
<STestSpec.HeaderDot $passed={false} /> | ||
{assertionsFailed} | ||
</STestSpec.HeaderDetail> | ||
)} | ||
</div> | ||
</S.DetailsWrapper> | ||
|
||
<S.SpanHeaderContainer> | ||
<S.HeaderItem> | ||
<SettingOutlined /> | ||
<S.HeaderItemText>{`${service} ${kind}`}</S.HeaderItemText> | ||
</S.HeaderItem> | ||
{Boolean(system) && ( | ||
<S.HeaderItem> | ||
<ToolOutlined /> | ||
<S.HeaderItemText>{system}</S.HeaderItemText> | ||
</S.HeaderItem> | ||
)} | ||
|
||
<S.HeaderItem> | ||
<ClockCircleOutlined /> | ||
<S.HeaderItemText>{duration}</S.HeaderItemText> | ||
</S.HeaderItem> | ||
</S.SpanHeaderContainer> | ||
</> | ||
); | ||
}; | ||
|
||
export default Detail; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import Span from 'models/Span.model'; | ||
import {ArrowLeftOutlined} from '@ant-design/icons'; | ||
import {Button, Divider} from 'antd'; | ||
import * as S from './SpanResultDetail.styled'; | ||
import Detail from './Detail'; | ||
|
||
interface IProps { | ||
assertionsFailed: number; | ||
assertionsPassed: number; | ||
onClose(): void; | ||
span: Span; | ||
} | ||
|
||
const Header = ({span, assertionsFailed, assertionsPassed, onClose}: IProps) => ( | ||
<> | ||
<S.HeaderContainer> | ||
<S.Row $hasGap> | ||
<Button icon={<ArrowLeftOutlined />} onClick={onClose} type="link" /> | ||
<S.HeaderTitle level={2}>Span Result Detail</S.HeaderTitle> | ||
</S.Row> | ||
</S.HeaderContainer> | ||
<Divider /> | ||
<Detail span={span} assertionsFailed={assertionsFailed} assertionsPassed={assertionsPassed} /> | ||
<Divider /> | ||
</> | ||
); | ||
|
||
export default Header; |
121 changes: 121 additions & 0 deletions
121
web/src/components/SpanResultDetail/SpanResultDetail.styled.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import {CheckCircleFilled, CloseCircleFilled, MinusCircleFilled} from '@ant-design/icons'; | ||
import {Drawer, Typography} from 'antd'; | ||
import styled from 'styled-components'; | ||
import {SemanticGroupNames, SemanticGroupNamesToColor} from 'constants/SemanticGroupNames.constants'; | ||
|
||
export const AssertionsContainer = styled.div` | ||
cursor: pointer; | ||
display: flex; | ||
flex-direction: column; | ||
gap: 16px; | ||
border-top: 1px solid ${({theme}) => theme.color.borderLight}; | ||
`; | ||
|
||
export const AssertionContainer = styled.div` | ||
span { | ||
overflow-wrap: anywhere; | ||
} | ||
`; | ||
|
||
export const DrawerContainer = styled(Drawer)<{$type: SemanticGroupNames}>` | ||
position: absolute; | ||
overflow: hidden; | ||
.ant-drawer-body { | ||
display: flex; | ||
flex-direction: column; | ||
border-top: ${({$type}) => `4px solid ${SemanticGroupNamesToColor[$type]}`}; | ||
} | ||
`; | ||
|
||
export const DrawerRow = styled.div` | ||
flex: 1; | ||
`; | ||
|
||
export const GridContainer = styled.div` | ||
display: grid; | ||
grid-template-columns: 4.5% 1fr; | ||
align-items: center; | ||
`; | ||
|
||
export const CheckItemContainer = styled.div<{$isSuccessful: boolean}>` | ||
padding: 10px 12px; | ||
border: 1px solid ${({theme}) => theme.color.borderLight}; | ||
border-top: ${({$isSuccessful, theme}) => `4px solid ${$isSuccessful ? theme.color.success : theme.color.error}`}; | ||
`; | ||
|
||
export const HeaderContainer = styled.div` | ||
align-items: center; | ||
display: flex; | ||
justify-content: space-between; | ||
`; | ||
|
||
export const HeaderItem = styled.div` | ||
align-items: center; | ||
color: ${({theme}) => theme.color.text}; | ||
display: flex; | ||
font-size: ${({theme}) => theme.size.sm}; | ||
`; | ||
|
||
export const HeaderItemText = styled(Typography.Text)` | ||
color: inherit; | ||
margin-left: 5px; | ||
`; | ||
|
||
export const HeaderTitle = styled(Typography.Title)` | ||
&& { | ||
text-overflow: ellipsis; | ||
max-width: 250px; | ||
text-wrap: nowrap; | ||
overflow: hidden; | ||
margin-bottom: 0; | ||
} | ||
`; | ||
|
||
export const IconError = styled(MinusCircleFilled)` | ||
color: ${({theme}) => theme.color.error}; | ||
`; | ||
|
||
export const IconSuccess = styled(CheckCircleFilled)` | ||
color: ${({theme}) => theme.color.success}; | ||
`; | ||
|
||
export const Row = styled.div<{$align?: string; $hasGap?: boolean; $justify?: string}>` | ||
align-items: ${({$align}) => $align || 'center'}; | ||
display: flex; | ||
justify-content: ${({$justify}) => $justify || 'flex-start'}; | ||
gap: ${({$hasGap}) => $hasGap && '8px'}; | ||
`; | ||
|
||
export const SecondaryText = styled(Typography.Text)` | ||
color: ${({theme}) => theme.color.textSecondary}; | ||
font-size: ${({theme}) => theme.size.sm}; | ||
`; | ||
|
||
export const SpanHeaderContainer = styled.div` | ||
align-items: center; | ||
cursor: pointer; | ||
display: flex; | ||
gap: 8px; | ||
`; | ||
|
||
export const DetailsWrapper = styled.div` | ||
align-items: center; | ||
cursor: pointer; | ||
justify-content: space-between; | ||
display: flex; | ||
`; | ||
|
||
export const ClearSearchIcon = styled(CloseCircleFilled)` | ||
position: absolute; | ||
right: 8px; | ||
top: 8px; | ||
color: ${({theme}) => theme.color.textLight}; | ||
cursor: pointer; | ||
`; | ||
|
||
export const SearchContainer = styled(Row)` | ||
margin-bottom: 16px; | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import {useTestRun} from 'providers/TestRun/TestRun.provider'; | ||
import {ISpanResult} from 'types/TestSpecs.types'; | ||
import * as S from './SpanResultDetail.styled'; | ||
import Content from './Content'; | ||
|
||
interface IProps { | ||
isOpen: boolean; | ||
onClose(): void; | ||
spanResult?: ISpanResult; | ||
} | ||
|
||
const SpanResultDetail = ({isOpen, onClose, spanResult}: IProps) => { | ||
const { | ||
run: {trace}, | ||
} = useTestRun(); | ||
|
||
if (!spanResult) return null; | ||
|
||
const span = trace?.flat[spanResult.spanId] || {}; | ||
|
||
return ( | ||
<S.DrawerContainer | ||
closable={false} | ||
getContainer={false} | ||
mask={false} | ||
onClose={onClose} | ||
placement="right" | ||
visible={isOpen} | ||
width="100%" | ||
height="100%" | ||
$type={span.type} | ||
> | ||
{!!spanResult && <Content onClose={onClose} span={span} checkResults={spanResult.checkResults} />} | ||
</S.DrawerContainer> | ||
); | ||
}; | ||
|
||
export default SpanResultDetail; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// eslint-disable-next-line no-restricted-exports | ||
export {default} from './SpanResultDetail'; |
Oops, something went wrong.