Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions static/app/icons/iconRectangle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as React from 'react';

import SvgIcon from './svgIcon';

type Props = React.ComponentProps<typeof SvgIcon>;

const IconRectangle = React.forwardRef(function IconRectangle(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add this to our storybook collection of icons?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sure, I can add it in a separate pr

props: Props,
ref: React.Ref<SVGSVGElement>
) {
return (
<SvgIcon {...props} ref={ref}>
<rect
x="6.38721"
y="0.341797"
width="9.03286"
height="9.03286"
rx="1.5"
transform="rotate(45 6.38721 0.341797)"
/>
</SvgIcon>
);
});

IconRectangle.displayName = 'IconRectangle';

export {IconRectangle};
1 change: 1 addition & 0 deletions static/app/icons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export {IconPrint} from './iconPrint';
export {IconProject} from './iconProject';
export {IconProjects} from './iconProjects';
export {IconQuestion} from './iconQuestion';
export {IconRectangle} from './iconRectangle';
export {IconRefresh} from './iconRefresh';
export {IconReleases} from './iconReleases';
export {IconReturn} from './iconReturn';
Expand Down
4 changes: 0 additions & 4 deletions static/app/views/alerts/incidentRules/ruleForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -578,10 +578,6 @@ class RuleFormContainer extends AsyncComponent<Props, State> {
this.setState({comparisonType: value, comparisonDelta, timeWindow});
};

handleComparisonDeltaChange = (value: number) => {
this.setState({comparisonDelta: value});
};

handleDeleteRule = async () => {
const {params} = this.props;
const {orgId, projectId, ruleId} = params;
Expand Down
117 changes: 83 additions & 34 deletions static/app/views/alerts/rules/details/body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,19 @@ import {parseSearch} from 'app/components/searchSyntax/parser';
import HighlightQuery from 'app/components/searchSyntax/renderer';
import TimeSince from 'app/components/timeSince';
import Tooltip from 'app/components/tooltip';
import {IconCheckmark, IconFire, IconInfo, IconWarning} from 'app/icons';
import {IconInfo, IconRectangle} from 'app/icons';
import {t, tct} from 'app/locale';
import overflowEllipsis from 'app/styles/overflowEllipsis';
import space from 'app/styles/space';
import {Actor, DateString, Organization, Project} from 'app/types';
import getDynamicText from 'app/utils/getDynamicText';
import Projects from 'app/utils/projects';
import {COMPARISON_DELTA_OPTIONS} from 'app/views/alerts/incidentRules/constants';
import {
Action,
AlertRuleThresholdType,
Dataset,
IncidentRule,
Trigger,
} from 'app/views/alerts/incidentRules/types';
import {extractEventTypeFilterFromRule} from 'app/views/alerts/incidentRules/utils/getEventTypeFilter';
import Timeline from 'app/views/alerts/rules/details/timeline';
Expand Down Expand Up @@ -127,36 +128,71 @@ export default class DetailsBody extends React.Component<Props> {
);
}

renderTrigger(trigger: Trigger): React.ReactNode {
renderTrigger(label: string, threshold: number, actions: Action[]): React.ReactNode {
const {rule} = this.props;

if (!rule) {
return null;
}

const status =
trigger.label === 'critical' ? (
<StatusWrapper>
<IconFire color="red300" size="sm" /> Critical
</StatusWrapper>
) : trigger.label === 'warning' ? (
<StatusWrapper>
<IconWarning color="yellow300" size="sm" /> Warning
</StatusWrapper>
label === 'critical'
? t('Critical')
: label === 'warning'
? t('Warning')
: t('Resolved');
const statusIcon =
label === 'critical' ? (
<StyledIconRectangle color="red300" size="sm" />
) : label === 'warning' ? (
<StyledIconRectangle color="yellow300" size="sm" />
) : (
<StatusWrapper>
<IconCheckmark color="green300" size="sm" isCircled /> Resolved
</StatusWrapper>
<StyledIconRectangle color="green300" size="sm" />
);

const thresholdTypeText =
rule.thresholdType === AlertRuleThresholdType.ABOVE ? t('above') : t('below');
const thresholdTypeText = (
label === 'resolved'
? rule.thresholdType === AlertRuleThresholdType.BELOW
: rule.thresholdType === AlertRuleThresholdType.ABOVE
)
? rule.comparisonDelta
? t('higher')
: t('above')
: rule.comparisonDelta
? t('lower')
: t('below');

const thresholdText = rule.comparisonDelta
? tct(
'When [threshold]% [comparisonType] in [timeWindow] compared to [comparisonDelta]',
{
threshold,
comparisonType: thresholdTypeText,
timeWindow: this.getTimeWindow(),
comparisonDelta: (
COMPARISON_DELTA_OPTIONS.find(
({value}) => value === rule.comparisonDelta
) ?? COMPARISON_DELTA_OPTIONS[0]
).label,
}
)
: tct('If [condition] in [timeWindow]', {
condition: `${thresholdTypeText} ${threshold}`,
timeWindow: this.getTimeWindow(),
});

return (
<TriggerCondition>
{status}
<TriggerText>{`${thresholdTypeText} ${trigger.alertThreshold}`}</TriggerText>
</TriggerCondition>
<TriggerConditionContainer>
{statusIcon}
<TriggerCondition>
{status}
<TriggerText>{thresholdText}</TriggerText>
{actions.map(
action =>
action.desc && <TriggerText key={action.id}>{action.desc}</TriggerText>
)}
</TriggerCondition>
</TriggerConditionContainer>
);
}

Expand Down Expand Up @@ -191,9 +227,21 @@ export default class DetailsBody extends React.Component<Props> {
</SidebarGroup>

<SidebarGroup>
<Heading>{t('Conditions')}</Heading>
{criticalTrigger && this.renderTrigger(criticalTrigger)}
{warningTrigger && this.renderTrigger(warningTrigger)}
<Heading>{t('Thresholds and Actions')}</Heading>
{typeof criticalTrigger?.alertThreshold === 'number' &&
this.renderTrigger(
criticalTrigger.label,
criticalTrigger.alertThreshold,
criticalTrigger.actions
)}
{typeof warningTrigger?.alertThreshold === 'number' &&
this.renderTrigger(
warningTrigger.label,
warningTrigger.alertThreshold,
warningTrigger.actions
)}
{typeof rule.resolveThreshold === 'number' &&
this.renderTrigger('resolved', rule.resolveThreshold, [])}
</SidebarGroup>

<SidebarGroup>
Expand Down Expand Up @@ -443,14 +491,6 @@ const DetailWrapper = styled('div')`
}
`;

const StatusWrapper = styled('div')`
display: flex;
align-items: center;
svg {
margin-right: ${space(0.5)};
}
`;

const HeaderContainer = styled('div')`
height: 60px;
display: flex;
Expand Down Expand Up @@ -548,16 +588,25 @@ const Filters = styled('span')`
font-family: ${p => p.theme.text.familyMono};
`;

const TriggerConditionContainer = styled('div')`
display: flex;
flex-direction: row;
`;

const TriggerCondition = styled('div')`
display: flex;
align-items: center;
flex-direction: column;
margin-left: ${space(0.75)};
`;

const TriggerText = styled('div')`
margin-left: ${space(0.5)};
white-space: nowrap;
color: ${p => p.theme.subText};
`;

const CreatedBy = styled('div')`
${overflowEllipsis}
`;

const StyledIconRectangle = styled(IconRectangle)`
margin-top: ${space(0.75)};
`;
4 changes: 2 additions & 2 deletions static/app/views/alerts/rules/details/metricChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -498,14 +498,14 @@ class MetricChart extends React.PureComponent<Props, State> {
}

let maxThresholdValue = 0;
if (warningTrigger?.alertThreshold) {
if (!rule.comparisonDelta && warningTrigger?.alertThreshold) {
const {alertThreshold} = warningTrigger;
const warningThresholdLine = createThresholdSeries(theme.yellow300, alertThreshold);
series.push(warningThresholdLine);
maxThresholdValue = Math.max(maxThresholdValue, alertThreshold);
}

if (criticalTrigger?.alertThreshold) {
if (!rule.comparisonDelta && criticalTrigger?.alertThreshold) {
const {alertThreshold} = criticalTrigger;
const criticalThresholdLine = createThresholdSeries(theme.red300, alertThreshold);
series.push(criticalThresholdLine);
Expand Down