Skip to content

Commit b9a0786

Browse files
authored
ref(aci): add attribute/tag branching on condition subfilters (#92335)
added a dropdown to select attribute or tag https://github.com/user-attachments/assets/766518cc-5914-4ae2-b24d-4865857526a6 also split each field into it's own component for readability and added a subfilter context to avoid passing `subfilter`, `subfilter_id`, and `onUpdate` to every child
1 parent 2b7cc97 commit b9a0786

File tree

1 file changed

+171
-77
lines changed

1 file changed

+171
-77
lines changed

static/app/views/automations/components/actionFilters/subfiltersList.tsx

Lines changed: 171 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {createContext, Fragment, useContext, useState} from 'react';
12
import styled from '@emotion/styled';
23
import {uuid4} from '@sentry/core';
34

@@ -9,9 +10,29 @@ import {PurpleTextButton} from 'sentry/components/workflowEngine/ui/purpleTextBu
910
import {IconAdd, IconDelete} from 'sentry/icons';
1011
import {t} from 'sentry/locale';
1112
import {space} from 'sentry/styles/space';
12-
import {MatchType} from 'sentry/views/automations/components/actionFilters/constants';
13+
import {DataConditionType} from 'sentry/types/workflowEngine/dataConditions';
14+
import {
15+
Attributes,
16+
MatchType,
17+
} from 'sentry/views/automations/components/actionFilters/constants';
1318
import {useDataConditionNodeContext} from 'sentry/views/automations/components/dataConditionNodes';
1419

20+
interface SubfilterProps {
21+
onUpdate: (comparison: Record<string, any>) => void;
22+
subfilter: Record<string, any>;
23+
subfilter_id: string;
24+
}
25+
26+
const SubfilterContext = createContext<SubfilterProps | null>(null);
27+
28+
function useSubfilterContext(): SubfilterProps {
29+
const context = useContext(SubfilterContext);
30+
if (!context) {
31+
throw new Error('useSubfilterContext was called outside of Subfilter');
32+
}
33+
return context;
34+
}
35+
1536
export function SubfiltersList() {
1637
const {condition, condition_id, onUpdate} = useDataConditionNodeContext();
1738

@@ -54,14 +75,20 @@ export function SubfiltersList() {
5475
<div>
5576
{subfilters.map((subfilter: Record<string, any>, i: number) => {
5677
return (
57-
<SubfilterRow
58-
subfilter={subfilter}
59-
subfilter_id={`${condition_id}.comparison.filters.${subfilter.id}`}
60-
onRemove={() => removeSubfilter(subfilter.id)}
61-
onUpdate={comparison => updateSubfilter(subfilter.id, comparison)}
78+
<SubfilterContext.Provider
79+
value={{
80+
subfilter,
81+
subfilter_id: `${condition_id}.comparison.filters.${subfilter.id}`,
82+
onUpdate: comparison => updateSubfilter(subfilter.id, comparison),
83+
}}
6284
key={subfilter.id}
63-
isLastRow={i === subfilterCount - 1}
64-
/>
85+
>
86+
<SubfilterRow
87+
onRemove={() => removeSubfilter(subfilter.id)}
88+
isFirstRow={i === 0}
89+
isLastRow={i === subfilterCount - 1}
90+
/>
91+
</SubfilterContext.Provider>
6592
);
6693
})}
6794
</div>
@@ -73,62 +100,18 @@ export function SubfiltersList() {
73100
}
74101

75102
interface SubfilterRowProps {
103+
isFirstRow: boolean;
104+
isLastRow: boolean;
76105
onRemove: () => void;
77-
onUpdate: (comparison: Record<string, any>) => void;
78-
subfilter: Record<string, any>;
79-
subfilter_id: string;
80-
isLastRow?: boolean;
81106
}
82107

83-
function SubfilterRow({
84-
subfilter,
85-
subfilter_id,
86-
onRemove,
87-
onUpdate,
88-
isLastRow,
89-
}: SubfilterRowProps) {
108+
function SubfilterRow({onRemove, isFirstRow, isLastRow}: SubfilterRowProps) {
90109
return (
91110
<RowWrapper>
92111
<Branch lastChild={isLastRow} />
93112
<StyledRowLine>
94-
<AutomationBuilderInputField
95-
name={`${subfilter_id}.key`}
96-
placeholder={t('key')}
97-
value={subfilter.key}
98-
onChange={(value: string) => {
99-
onUpdate({
100-
key: value,
101-
});
102-
}}
103-
/>
104-
<AutomationBuilderSelectField
105-
name={`${subfilter_id}.match`}
106-
value={subfilter.match}
107-
options={[
108-
{
109-
label: 'is',
110-
value: MatchType.EQUAL,
111-
},
112-
{
113-
label: 'is not',
114-
value: MatchType.NOT_EQUAL,
115-
},
116-
]}
117-
onChange={(value: MatchType) => {
118-
onUpdate({match: value});
119-
}}
120-
/>
121-
<AutomationBuilderInputField
122-
name={`${subfilter_id}.value`}
123-
placeholder={t('value')}
124-
value={`${subfilter.value}`}
125-
onChange={(value: string) => {
126-
onUpdate({
127-
value,
128-
});
129-
}}
130-
/>
131-
{!isLastRow && t('and')}
113+
{!isFirstRow && t('and')}
114+
<ComparisonTypeField />
132115
<Button
133116
aria-label={t('Delete Subfilter')}
134117
size="sm"
@@ -141,6 +124,137 @@ function SubfilterRow({
141124
);
142125
}
143126

127+
interface BranchProps {
128+
lastChild?: boolean;
129+
}
130+
131+
function Branch({lastChild}: BranchProps) {
132+
return (
133+
<svg
134+
width="26"
135+
height="38"
136+
viewBox="0 0 26 38"
137+
fill="none"
138+
xmlns="http://www.w3.org/2000/svg"
139+
>
140+
<line x1="0.5" x2="0.5" y2={lastChild ? '19' : '38'} stroke="#80708F" />
141+
<circle cx="23.5" cy="18.5" r="2.5" fill="#80708F" />
142+
<line x1="22" y1="18.5" x2="1" y2="18.5" stroke="#80708F" />
143+
</svg>
144+
);
145+
}
146+
147+
function ComparisonTypeField() {
148+
const {subfilter, subfilter_id} = useSubfilterContext();
149+
const [type, setType] = useState<DataConditionType | undefined>(undefined);
150+
151+
if (!type) {
152+
return (
153+
<AutomationBuilderSelectField
154+
name={`${subfilter_id}.comparison_type`}
155+
value={subfilter.comparison_type}
156+
placeholder={t('Select value type')}
157+
options={[
158+
{
159+
label: t('Attribute'),
160+
value: DataConditionType.EVENT_ATTRIBUTE,
161+
},
162+
{
163+
label: t('Tag'),
164+
value: DataConditionType.TAGGED_EVENT,
165+
},
166+
]}
167+
onChange={(value: DataConditionType) => {
168+
setType(value);
169+
}}
170+
/>
171+
);
172+
}
173+
174+
return (
175+
<Fragment>
176+
{type === DataConditionType.EVENT_ATTRIBUTE ? <AttributeField /> : <KeyField />}
177+
<MatchField />
178+
<ValueField />
179+
</Fragment>
180+
);
181+
}
182+
183+
function AttributeField() {
184+
const {subfilter, subfilter_id, onUpdate} = useSubfilterContext();
185+
return (
186+
<AutomationBuilderSelectField
187+
name={`${subfilter_id}.attribute`}
188+
placeholder={t('Select attribute')}
189+
value={subfilter.attribute}
190+
options={Object.values(Attributes).map(attribute => ({
191+
value: attribute,
192+
label: attribute,
193+
}))}
194+
onChange={(value: string) => {
195+
onUpdate({
196+
attribute: value,
197+
});
198+
}}
199+
/>
200+
);
201+
}
202+
203+
function KeyField() {
204+
const {subfilter, subfilter_id, onUpdate} = useSubfilterContext();
205+
return (
206+
<AutomationBuilderInputField
207+
name={`${subfilter_id}.key`}
208+
placeholder={t('Enter tag')}
209+
value={subfilter.key}
210+
onChange={(value: string) => {
211+
onUpdate({
212+
key: value,
213+
});
214+
}}
215+
/>
216+
);
217+
}
218+
219+
function MatchField() {
220+
const {subfilter, subfilter_id, onUpdate} = useSubfilterContext();
221+
return (
222+
<AutomationBuilderSelectField
223+
name={`${subfilter_id}.match`}
224+
value={subfilter.match}
225+
options={[
226+
{
227+
label: 'is',
228+
value: MatchType.EQUAL,
229+
},
230+
{
231+
label: 'is not',
232+
value: MatchType.NOT_EQUAL,
233+
},
234+
]}
235+
onChange={(value: MatchType) => {
236+
onUpdate({match: value});
237+
}}
238+
/>
239+
);
240+
}
241+
242+
function ValueField() {
243+
const {subfilter, subfilter_id, onUpdate} = useSubfilterContext();
244+
return (
245+
<AutomationBuilderInputField
246+
name={`${subfilter_id}.value`}
247+
placeholder={t('value')}
248+
value={`${subfilter.value}`}
249+
onChange={(value: string) => {
250+
onUpdate({
251+
value,
252+
});
253+
}}
254+
/>
255+
);
256+
}
257+
144258
const RowWrapper = styled('div')`
145259
display: flex;
146260
align-items: center;
@@ -164,23 +278,3 @@ const StyledRowLine = styled(RowLine)`
164278
}
165279
}
166280
`;
167-
168-
interface BranchProps {
169-
lastChild?: boolean;
170-
}
171-
172-
function Branch({lastChild}: BranchProps) {
173-
return (
174-
<svg
175-
width="26"
176-
height="38"
177-
viewBox="0 0 26 38"
178-
fill="none"
179-
xmlns="http://www.w3.org/2000/svg"
180-
>
181-
<line x1="0.5" x2="0.5" y2={lastChild ? '19' : '38'} stroke="#80708F" />
182-
<circle cx="23.5" cy="18.5" r="2.5" fill="#80708F" />
183-
<line x1="22" y1="18.5" x2="1" y2="18.5" stroke="#80708F" />
184-
</svg>
185-
);
186-
}

0 commit comments

Comments
 (0)